xref: /openbsd-src/sys/netmpls/mpls_output.c (revision 30aab16db3f9fe9f02749e53863a7de2b16a82bd)
1 /* $OpenBSD: mpls_output.c,v 1.16 2013/04/24 10:20:15 mpi 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 #ifdef INET6
37 #include <netinet/ip6.h>
38 #endif
39 
40 #ifdef MPLS_DEBUG
41 #define MPLS_LABEL_GET(l)	((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
42 #endif
43 
44 void		mpls_do_cksum(struct mbuf *);
45 u_int8_t	mpls_getttl(struct mbuf *, sa_family_t);
46 
47 int
48 mpls_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst,
49     struct rtentry *rt0)
50 {
51 	struct ifnet		*ifp = ifp0;
52 	struct sockaddr_mpls	*smpls;
53 	struct sockaddr_mpls	 sa_mpls;
54 	struct shim_hdr		*shim;
55 	struct rtentry		*rt = rt0;
56 	struct rt_mpls		*rt_mpls;
57 	int			 i, error;
58 	u_int8_t		 ttl;
59 
60 	if (rt0 == NULL || (dst->sa_family != AF_INET &&
61 	    dst->sa_family != AF_INET6 && dst->sa_family != AF_MPLS)) {
62 		if (!ISSET(ifp->if_xflags, IFXF_MPLS))
63 			return (ifp->if_output(ifp, m, dst, rt));
64 		else
65 			return (ifp->if_ll_output(ifp, m, dst, rt));
66 	}
67 
68 	/* need to calculate checksums now if necessary */
69 	mpls_do_cksum(m);
70 
71 	/* initialize sockaddr_mpls */
72 	bzero(&sa_mpls, sizeof(sa_mpls));
73 	smpls = &sa_mpls;
74 	smpls->smpls_family = AF_MPLS;
75 	smpls->smpls_len = sizeof(*smpls);
76 
77 	ttl = mpls_getttl(m, dst->sa_family);
78 
79 	for (i = 0; i < mpls_inkloop; i++) {
80 		rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
81 		if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
82 			/* no MPLS information for this entry */
83 			if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
84 #ifdef MPLS_DEBUG
85 				printf("MPLS_DEBUG: interface not mpls enabled\n");
86 #endif
87 				error = ENETUNREACH;
88 				goto bad;
89 			}
90 
91 			return (ifp->if_ll_output(ifp0, m, dst, rt0));
92 		}
93 
94 		switch (rt_mpls->mpls_operation) {
95 		case MPLS_OP_PUSH:
96 			m = mpls_shim_push(m, rt_mpls);
97 			break;
98 		case MPLS_OP_POP:
99 			m = mpls_shim_pop(m);
100 			break;
101 		case MPLS_OP_SWAP:
102 			m = mpls_shim_swap(m, rt_mpls);
103 			break;
104 		default:
105 			error = EINVAL;
106 			goto bad;
107 		}
108 
109 		if (m == NULL) {
110 			error = ENOBUFS;
111 			goto bad;
112 		}
113 
114 		/* refetch label */
115 		shim = mtod(m, struct shim_hdr *);
116 		/* mark first label with BOS flag */
117 		if (rt0 == rt && dst->sa_family != AF_MPLS)
118 			shim->shim_label |= MPLS_BOS_MASK;
119 
120 		ifp = rt->rt_ifp;
121 		if (ifp != NULL)
122 			break;
123 
124 		smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
125 		rt = rtalloc1(smplstosa(smpls), RT_REPORT, 0);
126 		if (rt == NULL) {
127 			/* no entry for this label */
128 #ifdef MPLS_DEBUG
129 			printf("MPLS_DEBUG: label %d not found\n",
130 			    MPLS_LABEL_GET(shim->shim_label));
131 #endif
132 			error = EHOSTUNREACH;
133 			goto bad;
134 		}
135 		rt->rt_use++;
136 		rt->rt_refcnt--;
137 	}
138 
139 	/* write back TTL */
140 	shim->shim_label &= ~MPLS_TTL_MASK;
141 	shim->shim_label |= htonl(ttl);
142 
143 #ifdef MPLS_DEBUG
144 	printf("MPLS: sending on %s outshim %x outlabel %d\n",
145 	    ifp->if_xname, ntohl(shim->shim_label),
146 	    MPLS_LABEL_GET(rt_mpls->mpls_label));
147 #endif
148 
149 	/* Output iface is not MPLS-enabled */
150 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
151 #ifdef MPLS_DEBUG
152 		printf("MPLS_DEBUG: interface not mpls enabled\n");
153 #endif
154 		error = ENETUNREACH;
155 		goto bad;
156 	}
157 
158 	/* reset broadcast and multicast flags, this is a P2P tunnel */
159 	m->m_flags &= ~(M_BCAST | M_MCAST);
160 
161 	smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
162 	return (ifp->if_ll_output(ifp, m, smplstosa(smpls), rt));
163 bad:
164 	if (m)
165 		m_freem(m);
166 	return (error);
167 }
168 
169 void
170 mpls_do_cksum(struct mbuf *m)
171 {
172 #ifdef INET
173 	struct ip *ip;
174 	u_int16_t hlen;
175 
176 	in_proto_cksum_out(m, NULL);
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 
187 u_int8_t
188 mpls_getttl(struct mbuf *m, sa_family_t af)
189 {
190 	struct shim_hdr *shim;
191 	struct ip *ip;
192 #ifdef INET6
193 	struct ip6_hdr *ip6hdr;
194 #endif
195 	u_int8_t ttl = mpls_defttl;
196 
197 	/* If the AF is MPLS then inherit the TTL from the present label. */
198 	if (af == AF_MPLS) {
199 		shim = mtod(m, struct shim_hdr *);
200 		ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
201 		return (ttl);
202 	}
203 	/* Else extract TTL from the encapsualted packet. */
204 	switch (*mtod(m, u_char *) >> 4) {
205 	case IPVERSION:
206 		if (!mpls_mapttl_ip)
207 			break;
208 		if (m->m_len < sizeof(*ip))
209 			break;			/* impossible */
210 		ip = mtod(m, struct ip *);
211 		ttl = ip->ip_ttl;
212 		break;
213 #ifdef INET6
214 	case IPV6_VERSION >> 4:
215 		if (!mpls_mapttl_ip6)
216 			break;
217 		if (m->m_len < sizeof(struct ip6_hdr))
218 			break;			/* impossible */
219 		ip6hdr = mtod(m, struct ip6_hdr *);
220 		ttl = ip6hdr->ip6_hlim;
221 		break;
222 #endif
223 	default:
224 		break;
225 	}
226 	return (ttl);
227 }
228