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