Line data Source code
1 : /* $OpenBSD: mpls_output.c,v 1.26 2015/12/02 08:47:00 claudio 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 0 : mpls_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
47 : struct rtentry *rt)
48 : {
49 : struct sockaddr_mpls *smpls;
50 0 : 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 0 : if (rt == NULL || (dst->sa_family != AF_INET &&
57 0 : dst->sa_family != AF_INET6 && dst->sa_family != AF_MPLS)) {
58 0 : if (!ISSET(ifp->if_xflags, IFXF_MPLS))
59 0 : return (ifp->if_output(ifp, m, dst, rt));
60 : else
61 0 : return (ifp->if_ll_output(ifp, m, dst, rt));
62 : }
63 :
64 : /* need to calculate checksums now if necessary */
65 0 : mpls_do_cksum(m);
66 :
67 : /* initialize sockaddr_mpls */
68 0 : bzero(&sa_mpls, sizeof(sa_mpls));
69 : smpls = &sa_mpls;
70 0 : smpls->smpls_family = AF_MPLS;
71 0 : smpls->smpls_len = sizeof(*smpls);
72 :
73 0 : ttl = mpls_getttl(m, dst->sa_family);
74 :
75 0 : rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
76 0 : if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
77 : /* no MPLS information for this entry */
78 0 : 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 0 : goto bad;
84 : }
85 :
86 0 : return (ifp->if_ll_output(ifp, m, dst, rt));
87 : }
88 :
89 : /* to be honest here only the push operation makes sense */
90 0 : switch (rt_mpls->mpls_operation) {
91 : case MPLS_OP_PUSH:
92 0 : m = mpls_shim_push(m, rt_mpls);
93 0 : break;
94 : case MPLS_OP_POP:
95 0 : m = mpls_shim_pop(m);
96 0 : break;
97 : case MPLS_OP_SWAP:
98 0 : m = mpls_shim_swap(m, rt_mpls);
99 0 : break;
100 : default:
101 : error = EINVAL;
102 0 : goto bad;
103 : }
104 :
105 0 : if (m == NULL) {
106 : error = ENOBUFS;
107 0 : goto bad;
108 : }
109 :
110 : /* refetch label */
111 0 : shim = mtod(m, struct shim_hdr *);
112 : /* mark first label with BOS flag */
113 0 : if (dst->sa_family != AF_MPLS)
114 0 : shim->shim_label |= MPLS_BOS_MASK;
115 :
116 : /* write back TTL */
117 0 : shim->shim_label &= ~MPLS_TTL_MASK;
118 0 : 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 0 : 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 0 : goto bad;
133 : }
134 :
135 : /* reset broadcast and multicast flags, this is a P2P tunnel */
136 0 : m->m_flags &= ~(M_BCAST | M_MCAST);
137 :
138 0 : smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
139 0 : error = ifp->if_ll_output(ifp, m, smplstosa(smpls), rt);
140 0 : return (error);
141 : bad:
142 0 : m_freem(m);
143 0 : return (error);
144 0 : }
145 :
146 : void
147 0 : mpls_do_cksum(struct mbuf *m)
148 : {
149 : struct ip *ip;
150 : u_int16_t hlen;
151 :
152 0 : in_proto_cksum_out(m, NULL);
153 :
154 0 : if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) {
155 0 : ip = mtod(m, struct ip *);
156 0 : hlen = ip->ip_hl << 2;
157 0 : ip->ip_sum = in_cksum(m, hlen);
158 0 : m->m_pkthdr.csum_flags &= ~M_IPV4_CSUM_OUT;
159 0 : }
160 0 : }
161 :
162 : u_int8_t
163 0 : mpls_getttl(struct mbuf *m, sa_family_t af)
164 : {
165 : struct shim_hdr *shim;
166 : struct ip *ip;
167 : #ifdef INET6
168 : struct ip6_hdr *ip6hdr;
169 : #endif
170 0 : u_int8_t ttl = mpls_defttl;
171 :
172 : /* If the AF is MPLS then inherit the TTL from the present label. */
173 0 : if (af == AF_MPLS) {
174 0 : shim = mtod(m, struct shim_hdr *);
175 0 : ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
176 0 : return (ttl);
177 : }
178 : /* Else extract TTL from the encapsualted packet. */
179 0 : switch (*mtod(m, u_char *) >> 4) {
180 : case IPVERSION:
181 0 : if (!mpls_mapttl_ip)
182 : break;
183 0 : if (m->m_len < sizeof(*ip))
184 : break; /* impossible */
185 0 : ip = mtod(m, struct ip *);
186 0 : ttl = ip->ip_ttl;
187 0 : break;
188 : #ifdef INET6
189 : case IPV6_VERSION >> 4:
190 0 : if (!mpls_mapttl_ip6)
191 : break;
192 0 : if (m->m_len < sizeof(struct ip6_hdr))
193 : break; /* impossible */
194 0 : ip6hdr = mtod(m, struct ip6_hdr *);
195 0 : ttl = ip6hdr->ip6_hlim;
196 0 : break;
197 : #endif
198 : default:
199 : break;
200 : }
201 0 : return (ttl);
202 0 : }
|