1 /* $OpenBSD: mpls_output.c,v 1.9 2010/05/28 12:09:10 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 (!mpls_enable || 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 shim = mtod(m, struct shim_hdr *); 121 smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; 122 123 rt = rtalloc1(smplstosa(smpls), RT_REPORT, 0); 124 if (rt == NULL) { 125 /* no entry for this label */ 126 #ifdef MPLS_DEBUG 127 printf("MPLS_DEBUG: label %d not found\n", 128 MPLS_LABEL_GET(shim->shim_label)); 129 #endif 130 error = EHOSTUNREACH; 131 goto bad; 132 } 133 rt->rt_use++; 134 rt->rt_refcnt--; 135 } 136 137 /* write back TTL */ 138 shim->shim_label &= ~MPLS_TTL_MASK; 139 shim->shim_label |= htonl(mpls_defttl); 140 141 #ifdef MPLS_DEBUG 142 printf("MPLS: sending on %s outshim %x outlabel %d\n", 143 ifp->if_xname, ntohl(shim->shim_label), 144 MPLS_LABEL_GET(rt_mpls->mpls_label)); 145 #endif 146 147 /* Output iface is not MPLS-enabled */ 148 if (!ISSET(ifp->if_xflags, IFXF_MPLS)) { 149 #ifdef MPLS_DEBUG 150 printf("MPLS_DEBUG: interface not mpls enabled\n"); 151 #endif 152 error = ENETUNREACH; 153 goto bad; 154 } 155 156 /* reset broadcast and multicast flags, this is a P2P tunnel */ 157 m->m_flags &= ~(M_BCAST | M_MCAST); 158 159 smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK; 160 return (ifp->if_ll_output(ifp, m, smplstosa(smpls), rt)); 161 bad: 162 if (m) 163 m_freem(m); 164 return (error); 165 } 166 167 void 168 mpls_do_cksum(struct mbuf *m) 169 { 170 #ifdef INET 171 struct ip *ip; 172 u_int16_t hlen; 173 174 if (m->m_pkthdr.csum_flags & (M_TCPV4_CSUM_OUT | M_UDPV4_CSUM_OUT)) { 175 in_delayed_cksum(m); 176 m->m_pkthdr.csum_flags &= ~(M_UDPV4_CSUM_OUT|M_TCPV4_CSUM_OUT); 177 } 178 if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) { 179 ip = mtod(m, struct ip *); 180 hlen = ip->ip_hl << 2; 181 ip->ip_sum = in_cksum(m, hlen); 182 m->m_pkthdr.csum_flags &= ~M_IPV4_CSUM_OUT; 183 } 184 #endif 185 } 186