xref: /openbsd/sys/netmpls/mpls_output.c (revision 09467b48)
1 /* $OpenBSD: mpls_output.c,v 1.28 2019/09/03 10:39:08 jsg 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/if_var.h>
27 #include <net/route.h>
28 
29 #include <netmpls/mpls.h>
30 
31 #include <netinet/in.h>
32 #include <netinet/ip.h>
33 
34 #ifdef INET6
35 #include <netinet/ip6.h>
36 #endif
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 u_int8_t	mpls_getttl(struct mbuf *, sa_family_t);
44 
45 int
46 mpls_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
47     struct rtentry *rt)
48 {
49 	struct sockaddr_mpls	*smpls;
50 	struct sockaddr_mpls	 sa_mpls;
51 	struct shim_hdr		*shim;
52 	struct rt_mpls		*rt_mpls;
53 	int			 error;
54 	u_int8_t		 ttl;
55 
56 	if (rt == 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 	mpls_do_cksum(m);
66 
67 	/* initialize sockaddr_mpls */
68 	bzero(&sa_mpls, sizeof(sa_mpls));
69 	smpls = &sa_mpls;
70 	smpls->smpls_family = AF_MPLS;
71 	smpls->smpls_len = sizeof(*smpls);
72 
73 	ttl = mpls_getttl(m, dst->sa_family);
74 
75 	rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
76 	if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
77 		/* no MPLS information for this entry */
78 		if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
79 #ifdef MPLS_DEBUG
80 			printf("MPLS_DEBUG: interface not mpls enabled\n");
81 #endif
82 			error = ENETUNREACH;
83 			goto bad;
84 		}
85 
86 		return (ifp->if_ll_output(ifp, m, dst, rt));
87 	}
88 
89 	/* to be honest here only the push operation makes sense */
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 (dst->sa_family != AF_MPLS)
114 		shim->shim_label |= MPLS_BOS_MASK;
115 
116 	/* write back TTL */
117 	shim->shim_label &= ~MPLS_TTL_MASK;
118 	shim->shim_label |= htonl(ttl);
119 
120 #ifdef MPLS_DEBUG
121 	printf("MPLS: sending on %s outshim %x outlabel %d\n",
122 	    ifp->if_xname, ntohl(shim->shim_label),
123 	    MPLS_LABEL_GET(rt_mpls->mpls_label));
124 #endif
125 
126 	/* Output iface is not MPLS-enabled */
127 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
128 #ifdef MPLS_DEBUG
129 		printf("MPLS_DEBUG: interface not mpls enabled\n");
130 #endif
131 		error = ENETUNREACH;
132 		goto bad;
133 	}
134 
135 	/* reset broadcast and multicast flags, this is a P2P tunnel */
136 	m->m_flags &= ~(M_BCAST | M_MCAST);
137 
138 	smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
139 	error = ifp->if_ll_output(ifp, m, smplstosa(smpls), rt);
140 	return (error);
141 bad:
142 	m_freem(m);
143 	return (error);
144 }
145 
146 void
147 mpls_do_cksum(struct mbuf *m)
148 {
149 	struct ip *ip;
150 	u_int16_t hlen;
151 
152 	in_proto_cksum_out(m, NULL);
153 
154 	if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) {
155 		ip = mtod(m, struct ip *);
156 		hlen = ip->ip_hl << 2;
157 		ip->ip_sum = in_cksum(m, hlen);
158 		m->m_pkthdr.csum_flags &= ~M_IPV4_CSUM_OUT;
159 	}
160 }
161 
162 u_int8_t
163 mpls_getttl(struct mbuf *m, sa_family_t af)
164 {
165 	struct mbuf *n;
166 	int loc, off;
167 	u_int8_t ttl = mpls_defttl;
168 
169 	/* If the AF is MPLS then inherit the TTL from the present label. */
170 	if (af == AF_MPLS)
171 		loc = 3;
172 	else {
173 		switch (*mtod(m, uint8_t *) >> 4) {
174 		case 4:
175 			if (!mpls_mapttl_ip)
176 				return (ttl);
177 
178 			loc = offsetof(struct ip, ip_ttl);
179 			break;
180 #ifdef INET6
181 		case 6:
182 			if (!mpls_mapttl_ip6)
183 				return (ttl);
184 
185 			loc = offsetof(struct ip6_hdr, ip6_hlim);
186 			break;
187 #endif
188 		default:
189 			return (ttl);
190 		}
191 	}
192 
193 	n = m_getptr(m, loc, &off);
194 	if (n == NULL)
195 		return (ttl);
196 
197 	ttl = *(mtod(n, uint8_t *) + off);
198 
199 	return (ttl);
200 }
201