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
42 u_int8_t mpls_getttl(struct mbuf *, sa_family_t);
43
44 int
mpls_output(struct ifnet * ifp,struct mbuf * m,struct sockaddr * dst,struct rtentry * rt)45 mpls_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);
142 bad:
143 m_freem(m);
144 return (error);
145 }
146
147 u_int8_t
mpls_getttl(struct mbuf * m,sa_family_t af)148 mpls_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