xref: /openbsd/sys/netmpls/mpls_output.c (revision 55055d61)
1*55055d61Sbluhm /* $OpenBSD: mpls_output.c,v 1.29 2023/05/13 13:35:18 bluhm Exp $ */
2bf64af94Smichele 
3bf64af94Smichele /*
4bf64af94Smichele  * Copyright (c) 2008 Claudio Jeker <claudio@openbsd.org>
5bf64af94Smichele  * Copyright (c) 2008 Michele Marchetto <michele@openbsd.org>
6bf64af94Smichele  *
7bf64af94Smichele  * Permission to use, copy, modify, and distribute this software for any
8bf64af94Smichele  * purpose with or without fee is hereby granted, provided that the above
9bf64af94Smichele  * copyright notice and this permission notice appear in all copies.
10bf64af94Smichele  *
11bf64af94Smichele  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12bf64af94Smichele  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13bf64af94Smichele  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14bf64af94Smichele  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15bf64af94Smichele  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16bf64af94Smichele  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17bf64af94Smichele  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18bf64af94Smichele  */
19bf64af94Smichele 
20bf64af94Smichele #include <sys/param.h>
21bf64af94Smichele #include <sys/mbuf.h>
22bf64af94Smichele #include <sys/systm.h>
23bf64af94Smichele #include <sys/socket.h>
24bf64af94Smichele 
25bf64af94Smichele #include <net/if.h>
260deb6685Smpi #include <net/if_var.h>
27bf64af94Smichele #include <net/route.h>
28bf64af94Smichele 
29bf64af94Smichele #include <netmpls/mpls.h>
30bf64af94Smichele 
31769a1feaSclaudio #include <netinet/in.h>
32769a1feaSclaudio #include <netinet/ip.h>
33769a1feaSclaudio 
34eb0ac13dSclaudio #ifdef INET6
35eb0ac13dSclaudio #include <netinet/ip6.h>
36eb0ac13dSclaudio #endif
37eb0ac13dSclaudio 
38bf64af94Smichele #ifdef MPLS_DEBUG
39bf64af94Smichele #define MPLS_LABEL_GET(l)	((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
40bf64af94Smichele #endif
41bf64af94Smichele 
42eb0ac13dSclaudio u_int8_t	mpls_getttl(struct mbuf *, sa_family_t);
43769a1feaSclaudio 
44769a1feaSclaudio int
mpls_output(struct ifnet * ifp,struct mbuf * m,struct sockaddr * dst,struct rtentry * rt)455b5e4895Sclaudio mpls_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
465b5e4895Sclaudio     struct rtentry *rt)
47bf64af94Smichele {
48bf64af94Smichele 	struct sockaddr_mpls	*smpls;
49cd776ce1Smichele 	struct sockaddr_mpls	 sa_mpls;
50bf64af94Smichele 	struct shim_hdr		*shim;
51cd776ce1Smichele 	struct rt_mpls		*rt_mpls;
525b5e4895Sclaudio 	int			 error;
53eb0ac13dSclaudio 	u_int8_t		 ttl;
54bf64af94Smichele 
555b5e4895Sclaudio 	if (rt == NULL || (dst->sa_family != AF_INET &&
56769a1feaSclaudio 	    dst->sa_family != AF_INET6 && dst->sa_family != AF_MPLS)) {
57769a1feaSclaudio 		if (!ISSET(ifp->if_xflags, IFXF_MPLS))
585b5e4895Sclaudio 			return (ifp->if_output(ifp, m, dst, rt));
59769a1feaSclaudio 		else
605b5e4895Sclaudio 			return (ifp->if_ll_output(ifp, m, dst, rt));
61bf64af94Smichele 	}
62bf64af94Smichele 
63769a1feaSclaudio 	/* need to calculate checksums now if necessary */
64*55055d61Sbluhm 	if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT)
65*55055d61Sbluhm 		in_hdr_cksum_out(m, NULL);
66*55055d61Sbluhm 	in_proto_cksum_out(m, NULL);
67bf64af94Smichele 
68769a1feaSclaudio 	/* initialize sockaddr_mpls */
69bf64af94Smichele 	bzero(&sa_mpls, sizeof(sa_mpls));
70bf64af94Smichele 	smpls = &sa_mpls;
71bf64af94Smichele 	smpls->smpls_family = AF_MPLS;
72bf64af94Smichele 	smpls->smpls_len = sizeof(*smpls);
73bf64af94Smichele 
74eb0ac13dSclaudio 	ttl = mpls_getttl(m, dst->sa_family);
75eb0ac13dSclaudio 
76cd776ce1Smichele 	rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
77cd776ce1Smichele 	if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
78cd776ce1Smichele 		/* no MPLS information for this entry */
79769a1feaSclaudio 		if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
80bf64af94Smichele #ifdef MPLS_DEBUG
81769a1feaSclaudio 			printf("MPLS_DEBUG: interface not mpls enabled\n");
82bf64af94Smichele #endif
83769a1feaSclaudio 			error = ENETUNREACH;
84cd776ce1Smichele 			goto bad;
85cd776ce1Smichele 		}
86bf64af94Smichele 
875b5e4895Sclaudio 		return (ifp->if_ll_output(ifp, m, dst, rt));
88769a1feaSclaudio 	}
8931dd9a49Smichele 
905b5e4895Sclaudio 	/* to be honest here only the push operation makes sense */
91769a1feaSclaudio 	switch (rt_mpls->mpls_operation) {
92bf64af94Smichele 	case MPLS_OP_PUSH:
93cd776ce1Smichele 		m = mpls_shim_push(m, rt_mpls);
94bf64af94Smichele 		break;
95cd776ce1Smichele 	case MPLS_OP_POP:
96dc9da2d3Smichele 		m = mpls_shim_pop(m);
97dc9da2d3Smichele 		break;
98bf64af94Smichele 	case MPLS_OP_SWAP:
99dc9da2d3Smichele 		m = mpls_shim_swap(m, rt_mpls);
100dc9da2d3Smichele 		break;
101bf64af94Smichele 	default:
102769a1feaSclaudio 		error = EINVAL;
103cd776ce1Smichele 		goto bad;
104bf64af94Smichele 	}
105bf64af94Smichele 
106769a1feaSclaudio 	if (m == NULL) {
107769a1feaSclaudio 		error = ENOBUFS;
108cd776ce1Smichele 		goto bad;
109769a1feaSclaudio 	}
110bf64af94Smichele 
111bf64af94Smichele 	/* refetch label */
112bf64af94Smichele 	shim = mtod(m, struct shim_hdr *);
113769a1feaSclaudio 	/* mark first label with BOS flag */
1145b5e4895Sclaudio 	if (dst->sa_family != AF_MPLS)
115769a1feaSclaudio 		shim->shim_label |= MPLS_BOS_MASK;
116bf64af94Smichele 
117bf64af94Smichele 	/* write back TTL */
118cd776ce1Smichele 	shim->shim_label &= ~MPLS_TTL_MASK;
119eb0ac13dSclaudio 	shim->shim_label |= htonl(ttl);
120bf64af94Smichele 
121bf64af94Smichele #ifdef MPLS_DEBUG
122cd776ce1Smichele 	printf("MPLS: sending on %s outshim %x outlabel %d\n",
123cd776ce1Smichele 	    ifp->if_xname, ntohl(shim->shim_label),
124cd776ce1Smichele 	    MPLS_LABEL_GET(rt_mpls->mpls_label));
125bf64af94Smichele #endif
126bf64af94Smichele 
127769a1feaSclaudio 	/* Output iface is not MPLS-enabled */
128769a1feaSclaudio 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
129769a1feaSclaudio #ifdef MPLS_DEBUG
130769a1feaSclaudio 		printf("MPLS_DEBUG: interface not mpls enabled\n");
131769a1feaSclaudio #endif
132769a1feaSclaudio 		error = ENETUNREACH;
133769a1feaSclaudio 		goto bad;
134769a1feaSclaudio 	}
135cd776ce1Smichele 
136769a1feaSclaudio 	/* reset broadcast and multicast flags, this is a P2P tunnel */
137769a1feaSclaudio 	m->m_flags &= ~(M_BCAST | M_MCAST);
138769a1feaSclaudio 
139769a1feaSclaudio 	smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
14006fd5c86Sclaudio 	error = ifp->if_ll_output(ifp, m, smplstosa(smpls), rt);
14106fd5c86Sclaudio 	return (error);
142cd776ce1Smichele bad:
143769a1feaSclaudio 	m_freem(m);
144769a1feaSclaudio 	return (error);
145769a1feaSclaudio }
146cd776ce1Smichele 
147eb0ac13dSclaudio u_int8_t
mpls_getttl(struct mbuf * m,sa_family_t af)148eb0ac13dSclaudio mpls_getttl(struct mbuf *m, sa_family_t af)
149eb0ac13dSclaudio {
150f2658839Sdlg 	struct mbuf *n;
151f2658839Sdlg 	int loc, off;
152eb0ac13dSclaudio 	u_int8_t ttl = mpls_defttl;
153eb0ac13dSclaudio 
154eb0ac13dSclaudio 	/* If the AF is MPLS then inherit the TTL from the present label. */
155f2658839Sdlg 	if (af == AF_MPLS)
156f2658839Sdlg 		loc = 3;
157f2658839Sdlg 	else {
158f2658839Sdlg 		switch (*mtod(m, uint8_t *) >> 4) {
159f2658839Sdlg 		case 4:
160eb0ac13dSclaudio 			if (!mpls_mapttl_ip)
161f2658839Sdlg 				return (ttl);
162f2658839Sdlg 
163f2658839Sdlg 			loc = offsetof(struct ip, ip_ttl);
164eb0ac13dSclaudio 			break;
16541a5659aSmikeb #ifdef INET6
166f2658839Sdlg 		case 6:
167eb0ac13dSclaudio 			if (!mpls_mapttl_ip6)
1681b0afb17Sjsg 				return (ttl);
169f2658839Sdlg 
170f2658839Sdlg 			loc = offsetof(struct ip6_hdr, ip6_hlim);
171eb0ac13dSclaudio 			break;
17241a5659aSmikeb #endif
173eb0ac13dSclaudio 		default:
174f2658839Sdlg 			return (ttl);
175eb0ac13dSclaudio 		}
176f2658839Sdlg 	}
177f2658839Sdlg 
178f2658839Sdlg 	n = m_getptr(m, loc, &off);
179f2658839Sdlg 	if (n == NULL)
180f2658839Sdlg 		return (ttl);
181f2658839Sdlg 
182f2658839Sdlg 	ttl = *(mtod(n, uint8_t *) + off);
183f2658839Sdlg 
184eb0ac13dSclaudio 	return (ttl);
185eb0ac13dSclaudio }
186