]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/ip_fastfwd.c
Add a MULTIDELAY option to allow the ARM kernel to have multiple DELAY
[FreeBSD/FreeBSD.git] / sys / netinet / ip_fastfwd.c
1 /*-
2  * Copyright (c) 2003 Andre Oppermann, Internet Business Solutions AG
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  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote
14  *    products derived from this software without specific prior written
15  *    permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 /*
31  * ip_fastforward gets its speed from processing the forwarded packet to
32  * completion (if_output on the other side) without any queues or netisr's.
33  * The receiving interface DMAs the packet into memory, the upper half of
34  * driver calls ip_fastforward, we do our routing table lookup and directly
35  * send it off to the outgoing interface, which DMAs the packet to the
36  * network card. The only part of the packet we touch with the CPU is the
37  * IP header (unless there are complex firewall rules touching other parts
38  * of the packet, but that is up to you). We are essentially limited by bus
39  * bandwidth and how fast the network card/driver can set up receives and
40  * transmits.
41  *
42  * We handle basic errors, IP header errors, checksum errors,
43  * destination unreachable, fragmentation and fragmentation needed and
44  * report them via ICMP to the sender.
45  *
46  * Else if something is not pure IPv4 unicast forwarding we fall back to
47  * the normal ip_input processing path. We should only be called from
48  * interfaces connected to the outside world.
49  *
50  * Firewalling is fully supported including divert, ipfw fwd and ipfilter
51  * ipnat and address rewrite.
52  *
53  * IPSEC is not supported if this host is a tunnel broker. IPSEC is
54  * supported for connections to/from local host.
55  *
56  * We try to do the least expensive (in CPU ops) checks and operations
57  * first to catch junk with as little overhead as possible.
58  * 
59  * We take full advantage of hardware support for IP checksum and
60  * fragmentation offloading.
61  *
62  * We don't do ICMP redirect in the fast forwarding path. I have had my own
63  * cases where two core routers with Zebra routing suite would send millions
64  * ICMP redirects to connected hosts if the destination router was not the
65  * default gateway. In one case it was filling the routing table of a host
66  * with approximately 300.000 cloned redirect entries until it ran out of
67  * kernel memory. However the networking code proved very robust and it didn't
68  * crash or fail in other ways.
69  */
70
71 /*
72  * Many thanks to Matt Thomas of NetBSD for basic structure of ip_flow.c which
73  * is being followed here.
74  */
75
76 #include <sys/cdefs.h>
77 __FBSDID("$FreeBSD$");
78
79 #include "opt_ipfw.h"
80 #include "opt_ipstealth.h"
81
82 #include <sys/param.h>
83 #include <sys/systm.h>
84 #include <sys/kernel.h>
85 #include <sys/malloc.h>
86 #include <sys/mbuf.h>
87 #include <sys/protosw.h>
88 #include <sys/sdt.h>
89 #include <sys/socket.h>
90 #include <sys/sysctl.h>
91
92 #include <net/pfil.h>
93 #include <net/if.h>
94 #include <net/if_types.h>
95 #include <net/if_var.h>
96 #include <net/if_dl.h>
97 #include <net/route.h>
98 #include <net/vnet.h>
99
100 #include <netinet/in.h>
101 #include <netinet/in_kdtrace.h>
102 #include <netinet/in_systm.h>
103 #include <netinet/in_var.h>
104 #include <netinet/ip.h>
105 #include <netinet/ip_var.h>
106 #include <netinet/ip_icmp.h>
107 #include <netinet/ip_options.h>
108
109 #include <machine/in_cksum.h>
110
111 static struct sockaddr_in *
112 ip_findroute(struct route *ro, struct in_addr dest, struct mbuf *m)
113 {
114         struct sockaddr_in *dst;
115         struct rtentry *rt;
116
117         /*
118          * Find route to destination.
119          */
120         bzero(ro, sizeof(*ro));
121         dst = (struct sockaddr_in *)&ro->ro_dst;
122         dst->sin_family = AF_INET;
123         dst->sin_len = sizeof(*dst);
124         dst->sin_addr.s_addr = dest.s_addr;
125         in_rtalloc_ign(ro, 0, M_GETFIB(m));
126
127         /*
128          * Route there and interface still up?
129          */
130         rt = ro->ro_rt;
131         if (rt && (rt->rt_flags & RTF_UP) &&
132             (rt->rt_ifp->if_flags & IFF_UP) &&
133             (rt->rt_ifp->if_drv_flags & IFF_DRV_RUNNING)) {
134                 if (rt->rt_flags & RTF_GATEWAY)
135                         dst = (struct sockaddr_in *)rt->rt_gateway;
136         } else {
137                 IPSTAT_INC(ips_noroute);
138                 IPSTAT_INC(ips_cantforward);
139                 if (rt)
140                         RTFREE(rt);
141                 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
142                 return NULL;
143         }
144         return dst;
145 }
146
147 /*
148  * Try to forward a packet based on the destination address.
149  * This is a fast path optimized for the plain forwarding case.
150  * If the packet is handled (and consumed) here then we return NULL;
151  * otherwise mbuf is returned and the packet should be delivered
152  * to ip_input for full processing.
153  */
154 struct mbuf *
155 ip_tryforward(struct mbuf *m)
156 {
157         struct ip *ip;
158         struct mbuf *m0 = NULL;
159         struct route ro;
160         struct sockaddr_in *dst = NULL;
161         struct ifnet *ifp;
162         struct in_addr odest, dest;
163         uint16_t ip_len, ip_off;
164         int error = 0;
165         int mtu;
166         struct m_tag *fwd_tag = NULL;
167
168         /*
169          * Are we active and forwarding packets?
170          */
171
172         M_ASSERTVALID(m);
173         M_ASSERTPKTHDR(m);
174
175         bzero(&ro, sizeof(ro));
176
177
178 #ifdef ALTQ
179         /*
180          * Is packet dropped by traffic conditioner?
181          */
182         if (altq_input != NULL && (*altq_input)(m, AF_INET) == 0)
183                 goto drop;
184 #endif
185
186         /*
187          * Only IP packets without options
188          */
189         ip = mtod(m, struct ip *);
190
191         if (ip->ip_hl != (sizeof(struct ip) >> 2)) {
192                 if (V_ip_doopts == 1)
193                         return m;
194                 else if (V_ip_doopts == 2) {
195                         icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_FILTER_PROHIB,
196                                 0, 0);
197                         return NULL;    /* mbuf already free'd */
198                 }
199                 /* else ignore IP options and continue */
200         }
201
202         /*
203          * Only unicast IP, not from loopback, no L2 or IP broadcast,
204          * no multicast, no INADDR_ANY
205          *
206          * XXX: Probably some of these checks could be direct drop
207          * conditions.  However it is not clear whether there are some
208          * hacks or obscure behaviours which make it neccessary to
209          * let ip_input handle it.  We play safe here and let ip_input
210          * deal with it until it is proven that we can directly drop it.
211          */
212         if ((m->m_flags & (M_BCAST|M_MCAST)) ||
213             (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) ||
214             ntohl(ip->ip_src.s_addr) == (u_long)INADDR_BROADCAST ||
215             ntohl(ip->ip_dst.s_addr) == (u_long)INADDR_BROADCAST ||
216             IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
217             IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
218             IN_LINKLOCAL(ntohl(ip->ip_src.s_addr)) ||
219             IN_LINKLOCAL(ntohl(ip->ip_dst.s_addr)) ||
220             ip->ip_src.s_addr == INADDR_ANY ||
221             ip->ip_dst.s_addr == INADDR_ANY )
222                 return m;
223
224         /*
225          * Is it for a local address on this host?
226          */
227         if (in_localip(ip->ip_dst))
228                 return m;
229
230         IPSTAT_INC(ips_total);
231
232         /*
233          * Step 3: incoming packet firewall processing
234          */
235
236         odest.s_addr = dest.s_addr = ip->ip_dst.s_addr;
237
238         /*
239          * Run through list of ipfilter hooks for input packets
240          */
241         if (!PFIL_HOOKED(&V_inet_pfil_hook))
242                 goto passin;
243
244         if (pfil_run_hooks(
245             &V_inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN, NULL) ||
246             m == NULL)
247                 goto drop;
248
249         M_ASSERTVALID(m);
250         M_ASSERTPKTHDR(m);
251
252         ip = mtod(m, struct ip *);      /* m may have changed by pfil hook */
253         dest.s_addr = ip->ip_dst.s_addr;
254
255         /*
256          * Destination address changed?
257          */
258         if (odest.s_addr != dest.s_addr) {
259                 /*
260                  * Is it now for a local address on this host?
261                  */
262                 if (in_localip(dest))
263                         goto forwardlocal;
264                 /*
265                  * Go on with new destination address
266                  */
267         }
268
269         if (m->m_flags & M_FASTFWD_OURS) {
270                 /*
271                  * ipfw changed it for a local address on this host.
272                  */
273                 goto forwardlocal;
274         }
275
276 passin:
277         /*
278          * Step 4: decrement TTL and look up route
279          */
280
281         /*
282          * Check TTL
283          */
284 #ifdef IPSTEALTH
285         if (!V_ipstealth) {
286 #endif
287         if (ip->ip_ttl <= IPTTLDEC) {
288                 icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, 0);
289                 return NULL;    /* mbuf already free'd */
290         }
291
292         /*
293          * Decrement the TTL and incrementally change the IP header checksum.
294          * Don't bother doing this with hw checksum offloading, it's faster
295          * doing it right here.
296          */
297         ip->ip_ttl -= IPTTLDEC;
298         if (ip->ip_sum >= (u_int16_t) ~htons(IPTTLDEC << 8))
299                 ip->ip_sum -= ~htons(IPTTLDEC << 8);
300         else
301                 ip->ip_sum += htons(IPTTLDEC << 8);
302 #ifdef IPSTEALTH
303         }
304 #endif
305
306         /*
307          * Find route to destination.
308          */
309         if ((dst = ip_findroute(&ro, dest, m)) == NULL)
310                 return NULL;    /* icmp unreach already sent */
311         ifp = ro.ro_rt->rt_ifp;
312
313         /*
314          * Immediately drop blackholed traffic, and directed broadcasts
315          * for either the all-ones or all-zero subnet addresses on
316          * locally attached networks.
317          */
318         if ((ro.ro_rt->rt_flags & (RTF_BLACKHOLE|RTF_BROADCAST)) != 0)
319                 goto drop;
320
321         /*
322          * Step 5: outgoing firewall packet processing
323          */
324
325         /*
326          * Run through list of hooks for output packets.
327          */
328         if (!PFIL_HOOKED(&V_inet_pfil_hook))
329                 goto passout;
330
331         if (pfil_run_hooks(&V_inet_pfil_hook, &m, ifp, PFIL_OUT, NULL) || m == NULL) {
332                 goto drop;
333         }
334
335         M_ASSERTVALID(m);
336         M_ASSERTPKTHDR(m);
337
338         ip = mtod(m, struct ip *);
339         dest.s_addr = ip->ip_dst.s_addr;
340
341         /*
342          * Destination address changed?
343          */
344         if (m->m_flags & M_IP_NEXTHOP)
345                 fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
346         if (odest.s_addr != dest.s_addr || fwd_tag != NULL) {
347                 /*
348                  * Is it now for a local address on this host?
349                  */
350                 if (m->m_flags & M_FASTFWD_OURS || in_localip(dest)) {
351 forwardlocal:
352                         /*
353                          * Return packet for processing by ip_input().
354                          */
355                         m->m_flags |= M_FASTFWD_OURS;
356                         if (ro.ro_rt)
357                                 RTFREE(ro.ro_rt);
358                         return m;
359                 }
360                 /*
361                  * Redo route lookup with new destination address
362                  */
363                 if (fwd_tag) {
364                         dest.s_addr = ((struct sockaddr_in *)
365                                     (fwd_tag + 1))->sin_addr.s_addr;
366                         m_tag_delete(m, fwd_tag);
367                         m->m_flags &= ~M_IP_NEXTHOP;
368                 }
369                 RTFREE(ro.ro_rt);
370                 if ((dst = ip_findroute(&ro, dest, m)) == NULL)
371                         return NULL;    /* icmp unreach already sent */
372                 ifp = ro.ro_rt->rt_ifp;
373         }
374
375 passout:
376         /*
377          * Step 6: send off the packet
378          */
379         ip_len = ntohs(ip->ip_len);
380         ip_off = ntohs(ip->ip_off);
381
382         /*
383          * Check if route is dampned (when ARP is unable to resolve)
384          */
385         if ((ro.ro_rt->rt_flags & RTF_REJECT) &&
386             (ro.ro_rt->rt_expire == 0 || time_uptime < ro.ro_rt->rt_expire)) {
387                 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
388                 goto consumed;
389         }
390
391         /*
392          * Check if media link state of interface is not down
393          */
394         if (ifp->if_link_state == LINK_STATE_DOWN) {
395                 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
396                 goto consumed;
397         }
398
399         /*
400          * Check if packet fits MTU or if hardware will fragment for us
401          */
402         if (ro.ro_rt->rt_mtu)
403                 mtu = min(ro.ro_rt->rt_mtu, ifp->if_mtu);
404         else
405                 mtu = ifp->if_mtu;
406
407         if (ip_len <= mtu) {
408                 /*
409                  * Avoid confusing lower layers.
410                  */
411                 m_clrprotoflags(m);
412                 /*
413                  * Send off the packet via outgoing interface
414                  */
415                 IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL);
416                 error = (*ifp->if_output)(ifp, m,
417                                 (struct sockaddr *)dst, &ro);
418         } else {
419                 /*
420                  * Handle EMSGSIZE with icmp reply needfrag for TCP MTU discovery
421                  */
422                 if (ip_off & IP_DF) {
423                         IPSTAT_INC(ips_cantfrag);
424                         icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
425                                 0, mtu);
426                         goto consumed;
427                 } else {
428                         /*
429                          * We have to fragment the packet
430                          */
431                         m->m_pkthdr.csum_flags |= CSUM_IP;
432                         if (ip_fragment(ip, &m, mtu, ifp->if_hwassist))
433                                 goto drop;
434                         KASSERT(m != NULL, ("null mbuf and no error"));
435                         /*
436                          * Send off the fragments via outgoing interface
437                          */
438                         error = 0;
439                         do {
440                                 m0 = m->m_nextpkt;
441                                 m->m_nextpkt = NULL;
442                                 /*
443                                  * Avoid confusing lower layers.
444                                  */
445                                 m_clrprotoflags(m);
446
447                                 IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL);
448                                 error = (*ifp->if_output)(ifp, m,
449                                         (struct sockaddr *)dst, &ro);
450                                 if (error)
451                                         break;
452                         } while ((m = m0) != NULL);
453                         if (error) {
454                                 /* Reclaim remaining fragments */
455                                 for (m = m0; m; m = m0) {
456                                         m0 = m->m_nextpkt;
457                                         m_freem(m);
458                                 }
459                         } else
460                                 IPSTAT_INC(ips_fragmented);
461                 }
462         }
463
464         if (error != 0)
465                 IPSTAT_INC(ips_odropped);
466         else {
467                 counter_u64_add(ro.ro_rt->rt_pksent, 1);
468                 IPSTAT_INC(ips_forward);
469                 IPSTAT_INC(ips_fastforward);
470         }
471 consumed:
472         RTFREE(ro.ro_rt);
473         return NULL;
474 drop:
475         if (m)
476                 m_freem(m);
477         if (ro.ro_rt)
478                 RTFREE(ro.ro_rt);
479         return NULL;
480 }