1/* $OpenBSD: mpls_output.c,v 1.29 2023/05/13 13:35:18 bluhm 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
42u_int8_t	mpls_getttl(struct mbuf *, sa_family_t);
43
44int
45mpls_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
46    struct rtentry *rt)
47{
48	struct sockaddr_mpls	*smpls;
49	struct sockaddr_mpls	 sa_mpls;
50	struct shim_hdr		*shim;
51	struct rt_mpls		*rt_mpls;
52	int			 error;
53	u_int8_t		 ttl;
54
55	if (rt == NULL || (dst->sa_family != AF_INET &&
56	    dst->sa_family != AF_INET6 && dst->sa_family != AF_MPLS)) {
57		if (!ISSET(ifp->if_xflags, IFXF_MPLS))
58			return (ifp->if_output(ifp, m, dst, rt));
59		else
60			return (ifp->if_ll_output(ifp, m, dst, rt));
61	}
62
63	/* need to calculate checksums now if necessary */
64	if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT)
65		in_hdr_cksum_out(m, NULL);
66	in_proto_cksum_out(m, NULL);
67
68	/* initialize sockaddr_mpls */
69	bzero(&sa_mpls, sizeof(sa_mpls));
70	smpls = &sa_mpls;
71	smpls->smpls_family = AF_MPLS;
72	smpls->smpls_len = sizeof(*smpls);
73
74	ttl = mpls_getttl(m, dst->sa_family);
75
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(ifp, m, dst, rt));
88	}
89
90	/* to be honest here only the push operation makes sense */
91	switch (rt_mpls->mpls_operation) {
92	case MPLS_OP_PUSH:
93		m = mpls_shim_push(m, rt_mpls);
94		break;
95	case MPLS_OP_POP:
96		m = mpls_shim_pop(m);
97		break;
98	case MPLS_OP_SWAP:
99		m = mpls_shim_swap(m, rt_mpls);
100		break;
101	default:
102		error = EINVAL;
103		goto bad;
104	}
105
106	if (m == NULL) {
107		error = ENOBUFS;
108		goto bad;
109	}
110
111	/* refetch label */
112	shim = mtod(m, struct shim_hdr *);
113	/* mark first label with BOS flag */
114	if (dst->sa_family != AF_MPLS)
115		shim->shim_label |= MPLS_BOS_MASK;
116
117	/* write back TTL */
118	shim->shim_label &= ~MPLS_TTL_MASK;
119	shim->shim_label |= htonl(ttl);
120
121#ifdef MPLS_DEBUG
122	printf("MPLS: sending on %s outshim %x outlabel %d\n",
123	    ifp->if_xname, ntohl(shim->shim_label),
124	    MPLS_LABEL_GET(rt_mpls->mpls_label));
125#endif
126
127	/* Output iface is not MPLS-enabled */
128	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
129#ifdef MPLS_DEBUG
130		printf("MPLS_DEBUG: interface not mpls enabled\n");
131#endif
132		error = ENETUNREACH;
133		goto bad;
134	}
135
136	/* reset broadcast and multicast flags, this is a P2P tunnel */
137	m->m_flags &= ~(M_BCAST | M_MCAST);
138
139	smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
140	error = ifp->if_ll_output(ifp, m, smplstosa(smpls), rt);
141	return (error);
142bad:
143	m_freem(m);
144	return (error);
145}
146
147u_int8_t
148mpls_getttl(struct mbuf *m, sa_family_t af)
149{
150	struct mbuf *n;
151	int loc, off;
152	u_int8_t ttl = mpls_defttl;
153
154	/* If the AF is MPLS then inherit the TTL from the present label. */
155	if (af == AF_MPLS)
156		loc = 3;
157	else {
158		switch (*mtod(m, uint8_t *) >> 4) {
159		case 4:
160			if (!mpls_mapttl_ip)
161				return (ttl);
162
163			loc = offsetof(struct ip, ip_ttl);
164			break;
165#ifdef INET6
166		case 6:
167			if (!mpls_mapttl_ip6)
168				return (ttl);
169
170			loc = offsetof(struct ip6_hdr, ip6_hlim);
171			break;
172#endif
173		default:
174			return (ttl);
175		}
176	}
177
178	n = m_getptr(m, loc, &off);
179	if (n == NULL)
180		return (ttl);
181
182	ttl = *(mtod(n, uint8_t *) + off);
183
184	return (ttl);
185}
186