summaryrefslogtreecommitdiffstats
path: root/sys/netmpls/mpls_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netmpls/mpls_output.c')
-rw-r--r--sys/netmpls/mpls_output.c148
1 files changed, 97 insertions, 51 deletions
diff --git a/sys/netmpls/mpls_output.c b/sys/netmpls/mpls_output.c
index ced18879ddc..59f221aae1e 100644
--- a/sys/netmpls/mpls_output.c
+++ b/sys/netmpls/mpls_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mpls_output.c,v 1.8 2010/05/07 13:33:17 claudio Exp $ */
+/* $OpenBSD: mpls_output.c,v 1.9 2010/05/28 12:09:10 claudio Exp $ */
/*
* Copyright (c) 2008 Claudio Jeker <claudio@openbsd.org>
@@ -27,66 +27,67 @@
#include <netmpls/mpls.h>
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
extern int mpls_inkloop;
#ifdef MPLS_DEBUG
#define MPLS_LABEL_GET(l) ((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
#endif
-struct mbuf *
-mpls_output(struct mbuf *m, struct rtentry *rt0)
+void mpls_do_cksum(struct mbuf *);
+
+int
+mpls_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst,
+ struct rtentry *rt0)
{
- struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ifnet *ifp = ifp0;
struct sockaddr_mpls *smpls;
struct sockaddr_mpls sa_mpls;
struct shim_hdr *shim;
struct rtentry *rt = rt0;
struct rt_mpls *rt_mpls;
- int i;
-
- if (!mpls_enable) {
- m_freem(m);
- goto bad;
+ int i, error;
+
+ if (!mpls_enable || rt0 == NULL || (dst->sa_family != AF_INET &&
+ dst->sa_family != AF_INET6 && dst->sa_family != AF_MPLS)) {
+ if (!ISSET(ifp->if_xflags, IFXF_MPLS))
+ return (ifp->if_output(ifp, m, dst, rt));
+ else
+ return (ifp->if_ll_output(ifp, m, dst, rt));
}
- /* reset broadcast and multicast flags, this is a P2P tunnel */
- m->m_flags &= ~(M_BCAST | M_MCAST);
-
- for (i = 0; i < mpls_inkloop; i++) {
- if (rt == NULL) {
- shim = mtod(m, struct shim_hdr *);
+ /* need to calculate checksums now if necessary */
+ if (m->m_pkthdr.csum_flags & (M_IPV4_CSUM_OUT | M_TCPV4_CSUM_OUT |
+ M_UDPV4_CSUM_OUT))
+ mpls_do_cksum(m);
- bzero(&sa_mpls, sizeof(sa_mpls));
- smpls = &sa_mpls;
- smpls->smpls_family = AF_MPLS;
- smpls->smpls_len = sizeof(*smpls);
- smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
-
- rt = rtalloc1(smplstosa(smpls), RT_REPORT, 0);
- if (rt == NULL) {
- /* no entry for this label */
-#ifdef MPLS_DEBUG
- printf("MPLS_DEBUG: label not found\n");
-#endif
- m_freem(m);
- goto bad;
- }
- rt->rt_use++;
- }
+ /* initialize sockaddr_mpls */
+ bzero(&sa_mpls, sizeof(sa_mpls));
+ smpls = &sa_mpls;
+ smpls->smpls_family = AF_MPLS;
+ smpls->smpls_len = sizeof(*smpls);
+ for (i = 0; i < mpls_inkloop; i++) {
rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
/* no MPLS information for this entry */
+ if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
#ifdef MPLS_DEBUG
- printf("MPLS_DEBUG: no MPLS information attached\n");
+ printf("MPLS_DEBUG: interface not mpls enabled\n");
#endif
- m_freem(m);
- goto bad;
- }
+ error = ENETUNREACH;
+ goto bad;
+ }
- switch (rt_mpls->mpls_operation & (MPLS_OP_PUSH | MPLS_OP_POP |
- MPLS_OP_SWAP)) {
+ return (ifp->if_ll_output(ifp0, m, dst, rt0));
+ }
+ switch (rt_mpls->mpls_operation) {
case MPLS_OP_PUSH:
m = mpls_shim_push(m, rt_mpls);
break;
@@ -97,29 +98,45 @@ mpls_output(struct mbuf *m, struct rtentry *rt0)
m = mpls_shim_swap(m, rt_mpls);
break;
default:
- m_freem(m);
+ error = EINVAL;
goto bad;
}
- if (m == NULL)
+ if (m == NULL) {
+ error = ENOBUFS;
goto bad;
+ }
/* refetch label */
shim = mtod(m, struct shim_hdr *);
- ifp = rt->rt_ifp;
+ /* mark first label with BOS flag */
+ if (rt0 == rt && dst->sa_family != AF_MPLS)
+ shim->shim_label |= MPLS_BOS_MASK;
+ ifp = rt->rt_ifp;
if (ifp != NULL)
break;
- if (rt0 != rt)
- RTFREE(rt);
+ shim = mtod(m, struct shim_hdr *);
+ smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
- rt = NULL;
+ rt = rtalloc1(smplstosa(smpls), RT_REPORT, 0);
+ if (rt == NULL) {
+ /* no entry for this label */
+#ifdef MPLS_DEBUG
+ printf("MPLS_DEBUG: label %d not found\n",
+ MPLS_LABEL_GET(shim->shim_label));
+#endif
+ error = EHOSTUNREACH;
+ goto bad;
+ }
+ rt->rt_use++;
+ rt->rt_refcnt--;
}
/* write back TTL */
shim->shim_label &= ~MPLS_TTL_MASK;
- shim->shim_label |= MPLS_BOS_MASK | htonl(mpls_defttl);
+ shim->shim_label |= htonl(mpls_defttl);
#ifdef MPLS_DEBUG
printf("MPLS: sending on %s outshim %x outlabel %d\n",
@@ -127,13 +144,42 @@ mpls_output(struct mbuf *m, struct rtentry *rt0)
MPLS_LABEL_GET(rt_mpls->mpls_label));
#endif
- if (rt != rt0)
- RTFREE(rt);
+ /* Output iface is not MPLS-enabled */
+ if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
+#ifdef MPLS_DEBUG
+ printf("MPLS_DEBUG: interface not mpls enabled\n");
+#endif
+ error = ENETUNREACH;
+ goto bad;
+ }
+
+ /* reset broadcast and multicast flags, this is a P2P tunnel */
+ m->m_flags &= ~(M_BCAST | M_MCAST);
- return (m);
+ smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
+ return (ifp->if_ll_output(ifp, m, smplstosa(smpls), rt));
bad:
- if (rt != rt0)
- RTFREE(rt);
+ if (m)
+ m_freem(m);
+ return (error);
+}
- return (NULL);
+void
+mpls_do_cksum(struct mbuf *m)
+{
+#ifdef INET
+ struct ip *ip;
+ u_int16_t hlen;
+
+ if (m->m_pkthdr.csum_flags & (M_TCPV4_CSUM_OUT | M_UDPV4_CSUM_OUT)) {
+ in_delayed_cksum(m);
+ m->m_pkthdr.csum_flags &= ~(M_UDPV4_CSUM_OUT|M_TCPV4_CSUM_OUT);
+ }
+ if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) {
+ ip = mtod(m, struct ip *);
+ hlen = ip->ip_hl << 2;
+ ip->ip_sum = in_cksum(m, hlen);
+ m->m_pkthdr.csum_flags &= ~M_IPV4_CSUM_OUT;
+ }
+#endif
}