1 /* $OpenBSD: mpls_output.c,v 1.11 2010/07/07 14:50:02 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2008 Michele Marchetto <michele@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/mbuf.h> 22 #include <sys/systm.h> 23 #include <sys/socket.h> 24 25 #include <net/if.h> 26 #include <net/route.h> 27 28 #include <netmpls/mpls.h> 29 30 #ifdef INET 31 #include <netinet/in.h> 32 #include <netinet/in_systm.h> 33 #include <netinet/ip.h> 34 #endif 35 36 extern int mpls_inkloop; 37 38 #ifdef MPLS_DEBUG 39 #define MPLS_LABEL_GET(l) ((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET) 40 #endif 41 42 void mpls_do_cksum(struct mbuf *); 43 44 int 45 mpls_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst, 46 struct rtentry *rt0) 47 { 48 struct ifnet *ifp = ifp0; 49 struct sockaddr_mpls *smpls; 50 struct sockaddr_mpls sa_mpls; 51 struct shim_hdr *shim; 52 struct rtentry *rt = rt0; 53 struct rt_mpls *rt_mpls; 54 int i, error; 55 56 if (rt0 == NULL || (dst->sa_family != AF_INET && 57 dst->sa_family != AF_INET6 && dst->sa_family != AF_MPLS)) { 58 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) 59 return (ifp->if_output(ifp, m, dst, rt)); 60 else 61 return (ifp->if_ll_output(ifp, m, dst, rt)); 62 } 63 64 /* need to calculate checksums now if necessary */ 65 if (m->m_pkthdr.csum_flags & (M_IPV4_CSUM_OUT | M_TCPV4_CSUM_OUT | 66 M_UDPV4_CSUM_OUT)) 67 mpls_do_cksum(m); 68 69 /* initialize sockaddr_mpls */ 70 bzero(&sa_mpls, sizeof(sa_mpls)); 71 smpls = &sa_mpls; 72 smpls->smpls_family = AF_MPLS; 73 smpls->smpls_len = sizeof(*smpls); 74 75 for (i = 0; i < mpls_inkloop; i++) { 76 rt_mpls = (struct rt_mpls *)rt->rt_llinfo; 77 if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) { 78 /* no MPLS information for this entry */ 79 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) { 80 #ifdef MPLS_DEBUG 81 printf("MPLS_DEBUG: interface not mpls enabled\n"); 82 #endif 83 error = ENETUNREACH; 84 goto bad; 85 } 86 87 return (ifp->if_ll_output(ifp0, m, dst, rt0)); 88 } 89 90 switch (rt_mpls->mpls_operation) { 91 case MPLS_OP_PUSH: 92 m = mpls_shim_push(m, rt_mpls); 93 break; 94 case MPLS_OP_POP: 95 m = mpls_shim_pop(m); 96 break; 97 case MPLS_OP_SWAP: 98 m = mpls_shim_swap(m, rt_mpls); 99 break; 100 default: 101 error = EINVAL; 102 goto bad; 103 } 104 105 if (m == NULL) { 106 error = ENOBUFS; 107 goto bad; 108 } 109 110 /* refetch label */ 111 shim = mtod(m, struct shim_hdr *); 112 /* mark first label with BOS flag */ 113 if (rt0 == rt && dst->sa_family != AF_MPLS) 114 shim->shim_label |= MPLS_BOS_MASK; 115 116 ifp = rt->rt_ifp; 117 if (ifp != NULL) 118 break; 119 120 smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; 121 rt = rtalloc1(smplstosa(smpls), RT_REPORT, 0); 122 if (rt == NULL) { 123 /* no entry for this label */ 124 #ifdef MPLS_DEBUG 125 printf("MPLS_DEBUG: label %d not found\n", 126 MPLS_LABEL_GET(shim->shim_label)); 127 #endif 128 error = EHOSTUNREACH; 129 goto bad; 130 } 131 rt->rt_use++; 132 rt->rt_refcnt--; 133 } 134 135 /* write back TTL */ 136 shim->shim_label &= ~MPLS_TTL_MASK; 137 shim->shim_label |= htonl(mpls_defttl); 138 139 #ifdef MPLS_DEBUG 140 printf("MPLS: sending on %s outshim %x outlabel %d\n", 141 ifp->if_xname, ntohl(shim->shim_label), 142 MPLS_LABEL_GET(rt_mpls->mpls_label)); 143 #endif 144 145 /* Output iface is not MPLS-enabled */ 146 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) { 147 #ifdef MPLS_DEBUG 148 printf("MPLS_DEBUG: interface not mpls enabled\n"); 149 #endif 150 error = ENETUNREACH; 151 goto bad; 152 } 153 154 /* reset broadcast and multicast flags, this is a P2P tunnel */ 155 m->m_flags &= ~(M_BCAST | M_MCAST); 156 157 smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; 158 return (ifp->if_ll_output(ifp, m, smplstosa(smpls), rt)); 159 bad: 160 if (m) 161 m_freem(m); 162 return (error); 163 } 164 165 void 166 mpls_do_cksum(struct mbuf *m) 167 { 168 #ifdef INET 169 struct ip *ip; 170 u_int16_t hlen; 171 172 if (m->m_pkthdr.csum_flags & (M_TCPV4_CSUM_OUT | M_UDPV4_CSUM_OUT)) { 173 in_delayed_cksum(m); 174 m->m_pkthdr.csum_flags &= ~(M_UDPV4_CSUM_OUT|M_TCPV4_CSUM_OUT); 175 } 176 if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) { 177 ip = mtod(m, struct ip *); 178 hlen = ip->ip_hl << 2; 179 ip->ip_sum = in_cksum(m, hlen); 180 m->m_pkthdr.csum_flags &= ~M_IPV4_CSUM_OUT; 181 } 182 #endif 183 } 184