summaryrefslogtreecommitdiffstats
path: root/sys/netmpls/mpls_output.c
diff options
context:
space:
mode:
authorclaudio <claudio@openbsd.org>2010-05-28 12:09:09 +0000
committerclaudio <claudio@openbsd.org>2010-05-28 12:09:09 +0000
commit769a1fea285f19c0a9cb41f1b4606a8c59697993 (patch)
treee2f32986198aeb7045f73aa001b35f6fd14e5807 /sys/netmpls/mpls_output.c
parentAdd missing initialization. (diff)
downloadwireguard-openbsd-769a1fea285f19c0a9cb41f1b4606a8c59697993.tar.xz
wireguard-openbsd-769a1fea285f19c0a9cb41f1b4606a8c59697993.zip
Rework the way we handle MPLS in the kernel. Instead of fumbling MPLS into
ether_output() and later on other L2 output functions use a trick and over- load the ifp->if_output() function pointer on MPLS enabled interfaces to go through mpls_output() which will then call the link level output function. By setting IFXF_MPLS on an interface the output pointers are switched. This now allows to cleanup the MPLS input and output pathes and fix mpe(4) so that the MPLS code now actually works for both P and PE systems. Tested by myself and michele (A custom kernel with MPLS and mpe enabled is still needed).
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
}