]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/netinet/ip_fastfwd.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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/socket.h>
89 #include <sys/sysctl.h>
90
91 #include <net/pfil.h>
92 #include <net/if.h>
93 #include <net/if_types.h>
94 #include <net/if_var.h>
95 #include <net/if_dl.h>
96 #include <net/route.h>
97
98 #include <netinet/in.h>
99 #include <netinet/in_systm.h>
100 #include <netinet/in_var.h>
101 #include <netinet/ip.h>
102 #include <netinet/ip_var.h>
103 #include <netinet/ip_icmp.h>
104 #include <netinet/ip_options.h>
105
106 #include <machine/in_cksum.h>
107
108 static int ipfastforward_active = 0;
109 SYSCTL_INT(_net_inet_ip, OID_AUTO, fastforwarding, CTLFLAG_RW,
110     &ipfastforward_active, 0, "Enable fast IP forwarding");
111
112 static struct sockaddr_in *
113 ip_findroute(struct route *ro, struct in_addr dest, struct mbuf *m)
114 {
115         struct sockaddr_in *dst;
116         struct rtentry *rt;
117
118         /*
119          * Find route to destination.
120          */
121         bzero(ro, sizeof(*ro));
122         dst = (struct sockaddr_in *)&ro->ro_dst;
123         dst->sin_family = AF_INET;
124         dst->sin_len = sizeof(*dst);
125         dst->sin_addr.s_addr = dest.s_addr;
126         in_rtalloc_ign(ro, RTF_CLONING, M_GETFIB(m));
127
128         /*
129          * Route there and interface still up?
130          */
131         rt = ro->ro_rt;
132         if (rt && (rt->rt_flags & RTF_UP) &&
133             (rt->rt_ifp->if_flags & IFF_UP) &&
134             (rt->rt_ifp->if_drv_flags & IFF_DRV_RUNNING)) {
135                 if (rt->rt_flags & RTF_GATEWAY)
136                         dst = (struct sockaddr_in *)rt->rt_gateway;
137         } else {
138                 ipstat.ips_noroute++;
139                 ipstat.ips_cantforward++;
140                 if (rt)
141                         RTFREE(rt);
142                 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
143                 return NULL;
144         }
145         return dst;
146 }
147
148 /*
149  * Try to forward a packet based on the destination address.
150  * This is a fast path optimized for the plain forwarding case.
151  * If the packet is handled (and consumed) here then we return 1;
152  * otherwise 0 is returned and the packet should be delivered
153  * to ip_input for full processing.
154  */
155 struct mbuf *
156 ip_fastforward(struct mbuf *m)
157 {
158         struct ip *ip;
159         struct mbuf *m0 = NULL;
160         struct route ro;
161         struct sockaddr_in *dst = NULL;
162         struct ifnet *ifp;
163         struct in_addr odest, dest;
164         u_short sum, ip_len;
165         int error = 0;
166         int hlen, mtu;
167 #ifdef IPFIREWALL_FORWARD
168         struct m_tag *fwd_tag;
169 #endif
170
171         /*
172          * Are we active and forwarding packets?
173          */
174         if (!ipfastforward_active || !ipforwarding)
175                 return m;
176
177         M_ASSERTVALID(m);
178         M_ASSERTPKTHDR(m);
179
180         ro.ro_rt = NULL;
181
182         /*
183          * Step 1: check for packet drop conditions (and sanity checks)
184          */
185
186         /*
187          * Is entire packet big enough?
188          */
189         if (m->m_pkthdr.len < sizeof(struct ip)) {
190                 ipstat.ips_tooshort++;
191                 goto drop;
192         }
193
194         /*
195          * Is first mbuf large enough for ip header and is header present?
196          */
197         if (m->m_len < sizeof (struct ip) &&
198            (m = m_pullup(m, sizeof (struct ip))) == NULL) {
199                 ipstat.ips_toosmall++;
200                 return NULL;    /* mbuf already free'd */
201         }
202
203         ip = mtod(m, struct ip *);
204
205         /*
206          * Is it IPv4?
207          */
208         if (ip->ip_v != IPVERSION) {
209                 ipstat.ips_badvers++;
210                 goto drop;
211         }
212
213         /*
214          * Is IP header length correct and is it in first mbuf?
215          */
216         hlen = ip->ip_hl << 2;
217         if (hlen < sizeof(struct ip)) { /* minimum header length */
218                 ipstat.ips_badlen++;
219                 goto drop;
220         }
221         if (hlen > m->m_len) {
222                 if ((m = m_pullup(m, hlen)) == NULL) {
223                         ipstat.ips_badhlen++;
224                         return NULL;    /* mbuf already free'd */
225                 }
226                 ip = mtod(m, struct ip *);
227         }
228
229         /*
230          * Checksum correct?
231          */
232         if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED)
233                 sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID);
234         else {
235                 if (hlen == sizeof(struct ip))
236                         sum = in_cksum_hdr(ip);
237                 else
238                         sum = in_cksum(m, hlen);
239         }
240         if (sum) {
241                 ipstat.ips_badsum++;
242                 goto drop;
243         }
244
245         /*
246          * Remember that we have checked the IP header and found it valid.
247          */
248         m->m_pkthdr.csum_flags |= (CSUM_IP_CHECKED | CSUM_IP_VALID);
249
250         ip_len = ntohs(ip->ip_len);
251
252         /*
253          * Is IP length longer than packet we have got?
254          */
255         if (m->m_pkthdr.len < ip_len) {
256                 ipstat.ips_tooshort++;
257                 goto drop;
258         }
259
260         /*
261          * Is packet longer than IP header tells us? If yes, truncate packet.
262          */
263         if (m->m_pkthdr.len > ip_len) {
264                 if (m->m_len == m->m_pkthdr.len) {
265                         m->m_len = ip_len;
266                         m->m_pkthdr.len = ip_len;
267                 } else
268                         m_adj(m, ip_len - m->m_pkthdr.len);
269         }
270
271         /*
272          * Is packet from or to 127/8?
273          */
274         if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
275             (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
276                 ipstat.ips_badaddr++;
277                 goto drop;
278         }
279
280 #ifdef ALTQ
281         /*
282          * Is packet dropped by traffic conditioner?
283          */
284         if (altq_input != NULL && (*altq_input)(m, AF_INET) == 0)
285                 goto drop;
286 #endif
287
288         /*
289          * Step 2: fallback conditions to normal ip_input path processing
290          */
291
292         /*
293          * Only IP packets without options
294          */
295         if (ip->ip_hl != (sizeof(struct ip) >> 2)) {
296                 if (ip_doopts == 1)
297                         return m;
298                 else if (ip_doopts == 2) {
299                         icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_FILTER_PROHIB,
300                                 0, 0);
301                         return NULL;    /* mbuf already free'd */
302                 }
303                 /* else ignore IP options and continue */
304         }
305
306         /*
307          * Only unicast IP, not from loopback, no L2 or IP broadcast,
308          * no multicast, no INADDR_ANY
309          *
310          * XXX: Probably some of these checks could be direct drop
311          * conditions.  However it is not clear whether there are some
312          * hacks or obscure behaviours which make it neccessary to
313          * let ip_input handle it.  We play safe here and let ip_input
314          * deal with it until it is proven that we can directly drop it.
315          */
316         if ((m->m_flags & (M_BCAST|M_MCAST)) ||
317             (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) ||
318             ntohl(ip->ip_src.s_addr) == (u_long)INADDR_BROADCAST ||
319             ntohl(ip->ip_dst.s_addr) == (u_long)INADDR_BROADCAST ||
320             IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
321             IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
322             IN_LINKLOCAL(ntohl(ip->ip_src.s_addr)) ||
323             IN_LINKLOCAL(ntohl(ip->ip_dst.s_addr)) ||
324             ip->ip_src.s_addr == INADDR_ANY ||
325             ip->ip_dst.s_addr == INADDR_ANY )
326                 return m;
327
328         /*
329          * Is it for a local address on this host?
330          */
331         if (in_localip(ip->ip_dst))
332                 return m;
333
334         ipstat.ips_total++;
335
336         /*
337          * Step 3: incoming packet firewall processing
338          */
339
340         /*
341          * Convert to host representation
342          */
343         ip->ip_len = ntohs(ip->ip_len);
344         ip->ip_off = ntohs(ip->ip_off);
345
346         odest.s_addr = dest.s_addr = ip->ip_dst.s_addr;
347
348         /*
349          * Run through list of ipfilter hooks for input packets
350          */
351         if (!PFIL_HOOKED(&inet_pfil_hook))
352                 goto passin;
353
354         if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN, NULL) ||
355             m == NULL)
356                 goto drop;
357
358         M_ASSERTVALID(m);
359         M_ASSERTPKTHDR(m);
360
361         ip = mtod(m, struct ip *);      /* m may have changed by pfil hook */
362         dest.s_addr = ip->ip_dst.s_addr;
363
364         /*
365          * Destination address changed?
366          */
367         if (odest.s_addr != dest.s_addr) {
368                 /*
369                  * Is it now for a local address on this host?
370                  */
371                 if (in_localip(dest))
372                         goto forwardlocal;
373                 /*
374                  * Go on with new destination address
375                  */
376         }
377 #ifdef IPFIREWALL_FORWARD
378         if (m->m_flags & M_FASTFWD_OURS) {
379                 /*
380                  * ipfw changed it for a local address on this host.
381                  */
382                 goto forwardlocal;
383         }
384 #endif /* IPFIREWALL_FORWARD */
385
386 passin:
387         /*
388          * Step 4: decrement TTL and look up route
389          */
390
391         /*
392          * Check TTL
393          */
394 #ifdef IPSTEALTH
395         if (!ipstealth) {
396 #endif
397         if (ip->ip_ttl <= IPTTLDEC) {
398                 icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, 0);
399                 return NULL;    /* mbuf already free'd */
400         }
401
402         /*
403          * Decrement the TTL and incrementally change the IP header checksum.
404          * Don't bother doing this with hw checksum offloading, it's faster
405          * doing it right here.
406          */
407         ip->ip_ttl -= IPTTLDEC;
408         if (ip->ip_sum >= (u_int16_t) ~htons(IPTTLDEC << 8))
409                 ip->ip_sum -= ~htons(IPTTLDEC << 8);
410         else
411                 ip->ip_sum += htons(IPTTLDEC << 8);
412 #ifdef IPSTEALTH
413         }
414 #endif
415
416         /*
417          * Find route to destination.
418          */
419         if ((dst = ip_findroute(&ro, dest, m)) == NULL)
420                 return NULL;    /* icmp unreach already sent */
421         ifp = ro.ro_rt->rt_ifp;
422
423         /*
424          * Immediately drop blackholed traffic, and directed broadcasts
425          * for either the all-ones or all-zero subnet addresses on
426          * locally attached networks.
427          */
428         if ((ro.ro_rt->rt_flags & (RTF_BLACKHOLE|RTF_BROADCAST)) != 0)
429                 goto drop;
430
431         /*
432          * Step 5: outgoing firewall packet processing
433          */
434
435         /*
436          * Run through list of hooks for output packets.
437          */
438         if (!PFIL_HOOKED(&inet_pfil_hook))
439                 goto passout;
440
441         if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_OUT, NULL) || m == NULL) {
442                 goto drop;
443         }
444
445         M_ASSERTVALID(m);
446         M_ASSERTPKTHDR(m);
447
448         ip = mtod(m, struct ip *);
449         dest.s_addr = ip->ip_dst.s_addr;
450
451         /*
452          * Destination address changed?
453          */
454 #ifndef IPFIREWALL_FORWARD
455         if (odest.s_addr != dest.s_addr) {
456 #else
457         fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
458         if (odest.s_addr != dest.s_addr || fwd_tag != NULL) {
459 #endif /* IPFIREWALL_FORWARD */
460                 /*
461                  * Is it now for a local address on this host?
462                  */
463 #ifndef IPFIREWALL_FORWARD
464                 if (in_localip(dest)) {
465 #else
466                 if (m->m_flags & M_FASTFWD_OURS || in_localip(dest)) {
467 #endif /* IPFIREWALL_FORWARD */
468 forwardlocal:
469                         /*
470                          * Return packet for processing by ip_input().
471                          * Keep host byte order as expected at ip_input's
472                          * "ours"-label.
473                          */
474                         m->m_flags |= M_FASTFWD_OURS;
475                         if (ro.ro_rt)
476                                 RTFREE(ro.ro_rt);
477                         return m;
478                 }
479                 /*
480                  * Redo route lookup with new destination address
481                  */
482 #ifdef IPFIREWALL_FORWARD
483                 if (fwd_tag) {
484                         dest.s_addr = ((struct sockaddr_in *)
485                                     (fwd_tag + 1))->sin_addr.s_addr;
486                         m_tag_delete(m, fwd_tag);
487                 }
488 #endif /* IPFIREWALL_FORWARD */
489                 RTFREE(ro.ro_rt);
490                 if ((dst = ip_findroute(&ro, dest, m)) == NULL)
491                         return NULL;    /* icmp unreach already sent */
492                 ifp = ro.ro_rt->rt_ifp;
493         }
494
495 passout:
496         /*
497          * Step 6: send off the packet
498          */
499
500         /*
501          * Check if route is dampned (when ARP is unable to resolve)
502          */
503         if ((ro.ro_rt->rt_flags & RTF_REJECT) &&
504             (ro.ro_rt->rt_rmx.rmx_expire == 0 ||
505             time_uptime < ro.ro_rt->rt_rmx.rmx_expire)) {
506                 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
507                 goto consumed;
508         }
509
510 #ifndef ALTQ
511         /*
512          * Check if there is enough space in the interface queue
513          */
514         if ((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >=
515             ifp->if_snd.ifq_maxlen) {
516                 ipstat.ips_odropped++;
517                 /* would send source quench here but that is depreciated */
518                 goto drop;
519         }
520 #endif
521
522         /*
523          * Check if media link state of interface is not down
524          */
525         if (ifp->if_link_state == LINK_STATE_DOWN) {
526                 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
527                 goto consumed;
528         }
529
530         /*
531          * Check if packet fits MTU or if hardware will fragment for us
532          */
533         if (ro.ro_rt->rt_rmx.rmx_mtu)
534                 mtu = min(ro.ro_rt->rt_rmx.rmx_mtu, ifp->if_mtu);
535         else
536                 mtu = ifp->if_mtu;
537
538         if (ip->ip_len <= mtu ||
539             (ifp->if_hwassist & CSUM_FRAGMENT && (ip->ip_off & IP_DF) == 0)) {
540                 /*
541                  * Restore packet header fields to original values
542                  */
543                 ip->ip_len = htons(ip->ip_len);
544                 ip->ip_off = htons(ip->ip_off);
545                 /*
546                  * Send off the packet via outgoing interface
547                  */
548                 error = (*ifp->if_output)(ifp, m,
549                                 (struct sockaddr *)dst, ro.ro_rt);
550         } else {
551                 /*
552                  * Handle EMSGSIZE with icmp reply needfrag for TCP MTU discovery
553                  */
554                 if (ip->ip_off & IP_DF) {
555                         ipstat.ips_cantfrag++;
556                         icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
557                                 0, mtu);
558                         goto consumed;
559                 } else {
560                         /*
561                          * We have to fragment the packet
562                          */
563                         m->m_pkthdr.csum_flags |= CSUM_IP;
564                         /*
565                          * ip_fragment expects ip_len and ip_off in host byte
566                          * order but returns all packets in network byte order
567                          */
568                         if (ip_fragment(ip, &m, mtu, ifp->if_hwassist,
569                                         (~ifp->if_hwassist & CSUM_DELAY_IP))) {
570                                 goto drop;
571                         }
572                         KASSERT(m != NULL, ("null mbuf and no error"));
573                         /*
574                          * Send off the fragments via outgoing interface
575                          */
576                         error = 0;
577                         do {
578                                 m0 = m->m_nextpkt;
579                                 m->m_nextpkt = NULL;
580
581                                 error = (*ifp->if_output)(ifp, m,
582                                         (struct sockaddr *)dst, ro.ro_rt);
583                                 if (error)
584                                         break;
585                         } while ((m = m0) != NULL);
586                         if (error) {
587                                 /* Reclaim remaining fragments */
588                                 for (m = m0; m; m = m0) {
589                                         m0 = m->m_nextpkt;
590                                         m_freem(m);
591                                 }
592                         } else
593                                 ipstat.ips_fragmented++;
594                 }
595         }
596
597         if (error != 0)
598                 ipstat.ips_odropped++;
599         else {
600                 ro.ro_rt->rt_rmx.rmx_pksent++;
601                 ipstat.ips_forward++;
602                 ipstat.ips_fastforward++;
603         }
604 consumed:
605         RTFREE(ro.ro_rt);
606         return NULL;
607 drop:
608         if (m)
609                 m_freem(m);
610         if (ro.ro_rt)
611                 RTFREE(ro.ro_rt);
612         return NULL;
613 }