diff options
Diffstat (limited to 'sys/netmpls/mpls_output.c')
-rw-r--r-- | sys/netmpls/mpls_output.c | 148 |
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 } |