xref: /openbsd-src/sys/netmpls/mpls_output.c (revision 769a1fea285f19c0a9cb41f1b4606a8c59697993)
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