]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet6/ip6_fastfwd.c
Merge bmake-20230510
[FreeBSD/FreeBSD.git] / sys / netinet6 / ip6_fastfwd.c
1 /*-
2  * Copyright (c) 2014-2016 Andrey V. Elsukov <ae@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "opt_inet6.h"
31 #include "opt_ipstealth.h"
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/mbuf.h>
36 #include <sys/socket.h>
37 #include <sys/kernel.h>
38 #include <sys/sysctl.h>
39
40 #include <net/if.h>
41 #include <net/if_var.h>
42 #include <net/if_private.h>
43 #include <net/route.h>
44 #include <net/route/nhop.h>
45 #include <net/pfil.h>
46 #include <net/vnet.h>
47
48 #include <netinet/in.h>
49 #include <netinet/in_kdtrace.h>
50 #include <netinet/in_var.h>
51 #include <netinet/ip_var.h>
52 #include <netinet/ip6.h>
53 #include <netinet/icmp6.h>
54 #include <netinet6/in6_var.h>
55 #include <netinet6/in6_fib.h>
56 #include <netinet6/ip6_var.h>
57 #include <netinet6/nd6.h>
58
59 static int
60 ip6_findroute(struct nhop_object **pnh, const struct sockaddr_in6 *dst,
61     struct mbuf *m)
62 {
63         struct nhop_object *nh;
64
65         nh = fib6_lookup(M_GETFIB(m), &dst->sin6_addr,
66             dst->sin6_scope_id, NHR_NONE, m->m_pkthdr.flowid);
67        if (nh == NULL) {
68                 IP6STAT_INC(ip6s_noroute);
69                 IP6STAT_INC(ip6s_cantforward);
70                 icmp6_error(m, ICMP6_DST_UNREACH,
71                     ICMP6_DST_UNREACH_NOROUTE, 0);
72                 return (EHOSTUNREACH);
73         }
74         if (nh->nh_flags & NHF_BLACKHOLE) {
75                 IP6STAT_INC(ip6s_cantforward);
76                 m_freem(m);
77                 return (EHOSTUNREACH);
78         }
79
80         if (nh->nh_flags & NHF_REJECT) {
81                 IP6STAT_INC(ip6s_cantforward);
82                 icmp6_error(m, ICMP6_DST_UNREACH,
83                     ICMP6_DST_UNREACH_REJECT, 0);
84                 return (EHOSTUNREACH);
85         }
86
87         *pnh = nh;
88
89         return (0);
90 }
91
92 struct mbuf*
93 ip6_tryforward(struct mbuf *m)
94 {
95         struct sockaddr_in6 dst;
96         struct nhop_object *nh;
97         struct m_tag *fwd_tag;
98         struct ip6_hdr *ip6;
99         struct ifnet *rcvif;
100         uint32_t plen;
101         int error;
102
103         /*
104          * Fallback conditions to ip6_input for slow path processing.
105          */
106         ip6 = mtod(m, struct ip6_hdr *);
107         if ((m->m_flags & (M_BCAST | M_MCAST)) != 0 ||
108             ip6->ip6_nxt == IPPROTO_HOPOPTS ||
109             IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
110             IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) ||
111             IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src) ||
112             IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) ||
113             in6_localip(&ip6->ip6_dst))
114                 return (m);
115         /*
116          * Check that the amount of data in the buffers
117          * is as at least much as the IPv6 header would have us expect.
118          * Trim mbufs if longer than we expect.
119          * Drop packet if shorter than we expect.
120          */
121         rcvif = m->m_pkthdr.rcvif;
122         plen = ntohs(ip6->ip6_plen);
123         if (plen == 0) {
124                 /*
125                  * Jumbograms must have hop-by-hop header and go via
126                  * slow path.
127                  */
128                 IP6STAT_INC(ip6s_badoptions);
129                 goto dropin;
130         }
131         if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) {
132                 IP6STAT_INC(ip6s_tooshort);
133                 in6_ifstat_inc(rcvif, ifs6_in_truncated);
134                 goto dropin;
135         }
136         if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) {
137                 if (m->m_len == m->m_pkthdr.len) {
138                         m->m_len = sizeof(struct ip6_hdr) + plen;
139                         m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen;
140                 } else
141                         m_adj(m, sizeof(struct ip6_hdr) + plen -
142                             m->m_pkthdr.len);
143         }
144
145         /*
146          * Hop limit.
147          */
148 #ifdef IPSTEALTH
149         if (!V_ip6stealth)
150 #endif
151         if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
152                 icmp6_error(m, ICMP6_TIME_EXCEEDED,
153                     ICMP6_TIME_EXCEED_TRANSIT, 0);
154                 m = NULL;
155                 goto dropin;
156         }
157
158         bzero(&dst, sizeof(dst));
159         dst.sin6_family = AF_INET6;
160         dst.sin6_len = sizeof(dst);
161         dst.sin6_addr = ip6->ip6_dst;
162
163         /*
164          * Incoming packet firewall processing.
165          */
166         if (!PFIL_HOOKED_IN(V_inet6_pfil_head))
167                 goto passin;
168         if (pfil_mbuf_in(V_inet6_pfil_head, &m, rcvif, NULL) !=
169             PFIL_PASS)
170                 goto dropin;
171         /*
172          * If packet filter sets the M_FASTFWD_OURS flag, this means
173          * that new destination or next hop is our local address.
174          * So, we can just go back to ip6_input.
175          * XXX: should we decrement ip6_hlim in such case?
176          *
177          * Also it can forward packet to another destination, e.g.
178          * M_IP6_NEXTHOP flag is set and fwd_tag is attached to mbuf.
179          */
180         if (m->m_flags & M_FASTFWD_OURS)
181                 return (m);
182
183         ip6 = mtod(m, struct ip6_hdr *);
184         if ((m->m_flags & M_IP6_NEXTHOP) &&
185             (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
186                 /*
187                  * Now we will find route to forwarded by pfil destination.
188                  */
189                 bcopy((fwd_tag + 1), &dst, sizeof(dst));
190                 m->m_flags &= ~M_IP6_NEXTHOP;
191                 m_tag_delete(m, fwd_tag);
192         } else {
193                 /* Update dst since pfil could change it */
194                 dst.sin6_addr = ip6->ip6_dst;
195         }
196 passin:
197         /*
198          * Find route to destination.
199          */
200         if (ip6_findroute(&nh, &dst, m) != 0) {
201                 m = NULL;
202                 in6_ifstat_inc(rcvif, ifs6_in_noroute);
203                 goto dropin;
204         }
205         if (!PFIL_HOOKED_OUT(V_inet6_pfil_head)) {
206                 if (m->m_pkthdr.len > nh->nh_mtu) {
207                         in6_ifstat_inc(nh->nh_ifp, ifs6_in_toobig);
208                         icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh->nh_mtu);
209                         m = NULL;
210                         goto dropout;
211                 }
212                 goto passout;
213         }
214
215         /*
216          * Outgoing packet firewall processing.
217          */
218         if (pfil_mbuf_out(V_inet6_pfil_head, &m, nh->nh_ifp,
219             NULL) != PFIL_PASS)
220                 goto dropout;
221
222         /*
223          * We used slow path processing for packets with scoped addresses.
224          * So, scope checks aren't needed here.
225          */
226         if (m->m_pkthdr.len > nh->nh_mtu) {
227                 in6_ifstat_inc(nh->nh_ifp, ifs6_in_toobig);
228                 icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh->nh_mtu);
229                 m = NULL;
230                 goto dropout;
231         }
232
233         /*
234          * If packet filter sets the M_FASTFWD_OURS flag, this means
235          * that new destination or next hop is our local address.
236          * So, we can just go back to ip6_input.
237          *
238          * Also it can forward packet to another destination, e.g.
239          * M_IP6_NEXTHOP flag is set and fwd_tag is attached to mbuf.
240          */
241         if (m->m_flags & M_FASTFWD_OURS) {
242                 /*
243                  * XXX: we did one hop and should decrement hop limit. But
244                  * now we are the destination and just don't pay attention.
245                  */
246                 return (m);
247         }
248         /*
249          * Again. A packet filter could change the destination address.
250          */
251         ip6 = mtod(m, struct ip6_hdr *);
252         if (m->m_flags & M_IP6_NEXTHOP)
253                 fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
254         else
255                 fwd_tag = NULL;
256
257         if (fwd_tag != NULL ||
258             !IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &ip6->ip6_dst)) {
259                 if (fwd_tag != NULL) {
260                         bcopy((fwd_tag + 1), &dst, sizeof(dst));
261                         m->m_flags &= ~M_IP6_NEXTHOP;
262                         m_tag_delete(m, fwd_tag);
263                 } else
264                         dst.sin6_addr = ip6->ip6_dst;
265                 /*
266                  * Redo route lookup with new destination address
267                  */
268                 if (ip6_findroute(&nh, &dst, m) != 0) {
269                         m = NULL;
270                         goto dropout;
271                 }
272         }
273 passout:
274 #ifdef IPSTEALTH
275         if (!V_ip6stealth)
276 #endif
277         {
278                 ip6->ip6_hlim -= IPV6_HLIMDEC;
279         }
280
281         m_clrprotoflags(m);     /* Avoid confusing lower layers. */
282         IP_PROBE(send, NULL, NULL, ip6, nh->nh_ifp, NULL, ip6);
283
284         if (nh->nh_flags & NHF_GATEWAY)
285                 dst.sin6_addr = nh->gw6_sa.sin6_addr;
286         error = (*nh->nh_ifp->if_output)(nh->nh_ifp, m,
287             (struct sockaddr *)&dst, NULL);
288         if (error != 0) {
289                 in6_ifstat_inc(nh->nh_ifp, ifs6_out_discard);
290                 IP6STAT_INC(ip6s_cantforward);
291         } else {
292                 in6_ifstat_inc(nh->nh_ifp, ifs6_out_forward);
293                 IP6STAT_INC(ip6s_forward);
294         }
295         return (NULL);
296 dropin:
297         in6_ifstat_inc(rcvif, ifs6_in_discard);
298         goto drop;
299 dropout:
300         in6_ifstat_inc(nh->nh_ifp, ifs6_out_discard);
301 drop:
302         if (m != NULL)
303                 m_freem(m);
304         return (NULL);
305 }