[BACK]Return to npf_inet.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / net / npf

Annotation of src/sys/net/npf/npf_inet.c, Revision 1.37.12.6

1.37.12.6! pgoyette    1: /*     $NetBSD: npf_inet.c,v 1.51 2018/08/31 14:16:06 maxv Exp $       */
1.1       rmind       2:
                      3: /*-
1.29      rmind       4:  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
1.1       rmind       5:  * All rights reserved.
                      6:  *
                      7:  * This material is based upon work partially supported by The
                      8:  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     21:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     22:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     23:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     24:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     25:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     26:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     27:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     28:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     29:  * POSSIBILITY OF SUCH DAMAGE.
                     30:  */
                     31:
                     32: /*
1.22      rmind      33:  * Various protocol related helper routines.
1.12      rmind      34:  *
                     35:  * This layer manipulates npf_cache_t structure i.e. caches requested headers
                     36:  * and stores which information was cached in the information bit field.
                     37:  * It is also responsibility of this layer to update or invalidate the cache
                     38:  * on rewrites (e.g. by translation routines).
1.1       rmind      39:  */
                     40:
1.36      christos   41: #ifdef _KERNEL
1.1       rmind      42: #include <sys/cdefs.h>
1.37.12.6! pgoyette   43: __KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.51 2018/08/31 14:16:06 maxv Exp $");
1.1       rmind      44:
                     45: #include <sys/param.h>
1.11      rmind      46: #include <sys/types.h>
1.1       rmind      47:
1.4       rmind      48: #include <net/pfil.h>
                     49: #include <net/if.h>
                     50: #include <net/ethertypes.h>
                     51: #include <net/if_ether.h>
                     52:
1.1       rmind      53: #include <netinet/in_systm.h>
                     54: #include <netinet/in.h>
1.33      mlelstv    55: #include <netinet6/in6_var.h>
1.1       rmind      56: #include <netinet/ip.h>
1.4       rmind      57: #include <netinet/ip6.h>
1.1       rmind      58: #include <netinet/tcp.h>
                     59: #include <netinet/udp.h>
                     60: #include <netinet/ip_icmp.h>
1.36      christos   61: #endif
1.1       rmind      62:
                     63: #include "npf_impl.h"
                     64:
                     65: /*
1.27      rmind      66:  * npf_fixup{16,32}_cksum: incremental update of the Internet checksum.
1.1       rmind      67:  */
                     68:
                     69: uint16_t
                     70: npf_fixup16_cksum(uint16_t cksum, uint16_t odatum, uint16_t ndatum)
                     71: {
                     72:        uint32_t sum;
                     73:
                     74:        /*
                     75:         * RFC 1624:
                     76:         *      HC' = ~(~HC + ~m + m')
1.27      rmind      77:         *
                     78:         * Note: 1's complement sum is endian-independent (RFC 1071, page 2).
1.1       rmind      79:         */
1.27      rmind      80:        sum = ~cksum & 0xffff;
                     81:        sum += (~odatum & 0xffff) + ndatum;
1.1       rmind      82:        sum = (sum >> 16) + (sum & 0xffff);
                     83:        sum += (sum >> 16);
                     84:
1.27      rmind      85:        return ~sum & 0xffff;
1.1       rmind      86: }
                     87:
                     88: uint16_t
                     89: npf_fixup32_cksum(uint16_t cksum, uint32_t odatum, uint32_t ndatum)
                     90: {
1.27      rmind      91:        uint32_t sum;
                     92:
                     93:        /*
                     94:         * Checksum 32-bit datum as as two 16-bit.  Note, the first
                     95:         * 32->16 bit reduction is not necessary.
                     96:         */
                     97:        sum = ~cksum & 0xffff;
                     98:        sum += (~odatum & 0xffff) + (ndatum & 0xffff);
1.1       rmind      99:
1.27      rmind     100:        sum += (~odatum >> 16) + (ndatum >> 16);
                    101:        sum = (sum >> 16) + (sum & 0xffff);
                    102:        sum += (sum >> 16);
                    103:        return ~sum & 0xffff;
1.1       rmind     104: }
                    105:
                    106: /*
1.4       rmind     107:  * npf_addr_cksum: calculate checksum of the address, either IPv4 or IPv6.
                    108:  */
                    109: uint16_t
1.19      rmind     110: npf_addr_cksum(uint16_t cksum, int sz, const npf_addr_t *oaddr,
                    111:     const npf_addr_t *naddr)
1.4       rmind     112: {
1.19      rmind     113:        const uint32_t *oip32 = (const uint32_t *)oaddr;
                    114:        const uint32_t *nip32 = (const uint32_t *)naddr;
1.4       rmind     115:
                    116:        KASSERT(sz % sizeof(uint32_t) == 0);
                    117:        do {
                    118:                cksum = npf_fixup32_cksum(cksum, *oip32++, *nip32++);
                    119:                sz -= sizeof(uint32_t);
                    120:        } while (sz);
                    121:
                    122:        return cksum;
                    123: }
                    124:
                    125: /*
1.26      rmind     126:  * npf_addr_sum: provide IP addresses as a XORed 32-bit integer.
1.4       rmind     127:  * Note: used for hash function.
1.1       rmind     128:  */
1.4       rmind     129: uint32_t
1.26      rmind     130: npf_addr_mix(const int sz, const npf_addr_t *a1, const npf_addr_t *a2)
1.1       rmind     131: {
1.4       rmind     132:        uint32_t mix = 0;
1.1       rmind     133:
1.5       rmind     134:        KASSERT(sz > 0 && a1 != NULL && a2 != NULL);
                    135:
1.26      rmind     136:        for (int i = 0; i < (sz >> 2); i++) {
1.36      christos  137:                mix ^= a1->word32[i];
                    138:                mix ^= a2->word32[i];
1.4       rmind     139:        }
                    140:        return mix;
                    141: }
1.1       rmind     142:
1.13      rmind     143: /*
                    144:  * npf_addr_mask: apply the mask to a given address and store the result.
                    145:  */
                    146: void
                    147: npf_addr_mask(const npf_addr_t *addr, const npf_netmask_t mask,
                    148:     const int alen, npf_addr_t *out)
1.12      rmind     149: {
1.13      rmind     150:        const int nwords = alen >> 2;
1.12      rmind     151:        uint_fast8_t length = mask;
                    152:
                    153:        /* Note: maximum length is 32 for IPv4 and 128 for IPv6. */
                    154:        KASSERT(length <= NPF_MAX_NETMASK);
                    155:
1.13      rmind     156:        for (int i = 0; i < nwords; i++) {
                    157:                uint32_t wordmask;
                    158:
1.12      rmind     159:                if (length >= 32) {
1.13      rmind     160:                        wordmask = htonl(0xffffffff);
1.12      rmind     161:                        length -= 32;
1.13      rmind     162:                } else if (length) {
                    163:                        wordmask = htonl(0xffffffff << (32 - length));
                    164:                        length = 0;
1.12      rmind     165:                } else {
1.13      rmind     166:                        wordmask = 0;
1.12      rmind     167:                }
1.36      christos  168:                out->word32[i] = addr->word32[i] & wordmask;
1.12      rmind     169:        }
                    170: }
                    171:
                    172: /*
                    173:  * npf_addr_cmp: compare two addresses, either IPv4 or IPv6.
                    174:  *
1.13      rmind     175:  * => Return 0 if equal and negative/positive if less/greater accordingly.
1.12      rmind     176:  * => Ignore the mask, if NPF_NO_NETMASK is specified.
                    177:  */
                    178: int
                    179: npf_addr_cmp(const npf_addr_t *addr1, const npf_netmask_t mask1,
1.13      rmind     180:     const npf_addr_t *addr2, const npf_netmask_t mask2, const int alen)
1.12      rmind     181: {
1.13      rmind     182:        npf_addr_t realaddr1, realaddr2;
1.12      rmind     183:
                    184:        if (mask1 != NPF_NO_NETMASK) {
1.13      rmind     185:                npf_addr_mask(addr1, mask1, alen, &realaddr1);
                    186:                addr1 = &realaddr1;
1.12      rmind     187:        }
                    188:        if (mask2 != NPF_NO_NETMASK) {
1.13      rmind     189:                npf_addr_mask(addr2, mask2, alen, &realaddr2);
                    190:                addr2 = &realaddr2;
1.12      rmind     191:        }
1.13      rmind     192:        return memcmp(addr1, addr2, alen);
1.12      rmind     193: }
                    194:
1.4       rmind     195: /*
                    196:  * npf_tcpsaw: helper to fetch SEQ, ACK, WIN and return TCP data length.
1.12      rmind     197:  *
                    198:  * => Returns all values in host byte-order.
1.4       rmind     199:  */
                    200: int
1.12      rmind     201: npf_tcpsaw(const npf_cache_t *npc, tcp_seq *seq, tcp_seq *ack, uint32_t *win)
1.4       rmind     202: {
1.19      rmind     203:        const struct tcphdr *th = npc->npc_l4.tcp;
1.8       rmind     204:        u_int thlen;
1.1       rmind     205:
1.7       zoltan    206:        KASSERT(npf_iscached(npc, NPC_TCP));
1.1       rmind     207:
1.4       rmind     208:        *seq = ntohl(th->th_seq);
                    209:        *ack = ntohl(th->th_ack);
                    210:        *win = (uint32_t)ntohs(th->th_win);
1.8       rmind     211:        thlen = th->th_off << 2;
1.1       rmind     212:
1.7       zoltan    213:        if (npf_iscached(npc, NPC_IP4)) {
1.19      rmind     214:                const struct ip *ip = npc->npc_ip.v4;
1.21      rmind     215:                return ntohs(ip->ip_len) - npc->npc_hlen - thlen;
1.12      rmind     216:        } else if (npf_iscached(npc, NPC_IP6)) {
1.19      rmind     217:                const struct ip6_hdr *ip6 = npc->npc_ip.v6;
1.37.12.2  pgoyette  218:                return ntohs(ip6->ip6_plen) -
                    219:                    (npc->npc_hlen - sizeof(*ip6)) - thlen;
1.7       zoltan    220:        }
                    221:        return 0;
1.1       rmind     222: }
                    223:
                    224: /*
1.4       rmind     225:  * npf_fetch_tcpopts: parse and return TCP options.
1.1       rmind     226:  */
                    227: bool
1.32      rmind     228: npf_fetch_tcpopts(npf_cache_t *npc, uint16_t *mss, int *wscale)
1.1       rmind     229: {
1.32      rmind     230:        nbuf_t *nbuf = npc->npc_nbuf;
1.19      rmind     231:        const struct tcphdr *th = npc->npc_l4.tcp;
1.37.12.5  pgoyette  232:        int cnt, optlen = 0;
                    233:        uint8_t *cp, opt;
1.4       rmind     234:        uint8_t val;
1.19      rmind     235:        bool ok;
1.4       rmind     236:
1.7       zoltan    237:        KASSERT(npf_iscached(npc, NPC_IP46));
                    238:        KASSERT(npf_iscached(npc, NPC_TCP));
1.10      rmind     239:
1.4       rmind     240:        /* Determine if there are any TCP options, get their length. */
1.37.12.5  pgoyette  241:        cnt = (th->th_off << 2) - sizeof(struct tcphdr);
                    242:        if (cnt <= 0) {
1.4       rmind     243:                /* No options. */
1.1       rmind     244:                return false;
1.4       rmind     245:        }
1.37.12.5  pgoyette  246:        KASSERT(cnt <= MAX_TCPOPTLEN);
1.1       rmind     247:
1.37.12.5  pgoyette  248:        /* Fetch all the options at once. */
1.19      rmind     249:        nbuf_reset(nbuf);
1.37.12.5  pgoyette  250:        const int step = npc->npc_hlen + sizeof(struct tcphdr);
                    251:        if ((cp = nbuf_advance(nbuf, step, cnt)) == NULL) {
1.19      rmind     252:                ok = false;
                    253:                goto done;
1.4       rmind     254:        }
1.12      rmind     255:
1.37.12.5  pgoyette  256:        /* Scan the options. */
                    257:        for (; cnt > 0; cnt -= optlen, cp += optlen) {
                    258:                opt = cp[0];
                    259:                if (opt == TCPOPT_EOL)
                    260:                        break;
                    261:                if (opt == TCPOPT_NOP)
                    262:                        optlen = 1;
                    263:                else {
                    264:                        if (cnt < 2)
                    265:                                break;
                    266:                        optlen = cp[1];
                    267:                        if (optlen < 2 || optlen > cnt)
                    268:                                break;
                    269:                }
                    270:
                    271:                switch (opt) {
                    272:                case TCPOPT_MAXSEG:
                    273:                        if (optlen != TCPOLEN_MAXSEG)
                    274:                                continue;
                    275:                        if (mss) {
1.37.12.6! pgoyette  276:                                memcpy(mss, cp + 2, sizeof(uint16_t));
1.19      rmind     277:                        }
1.37.12.5  pgoyette  278:                        break;
                    279:                case TCPOPT_WINDOW:
                    280:                        if (optlen != TCPOLEN_WINDOW)
                    281:                                continue;
                    282:                        val = *(cp + 2);
                    283:                        *wscale = (val > TCP_MAX_WINSHIFT) ? TCP_MAX_WINSHIFT : val;
                    284:                        break;
                    285:                default:
                    286:                        break;
1.4       rmind     287:                }
                    288:        }
1.37.12.5  pgoyette  289:
1.19      rmind     290:        ok = true;
                    291: done:
                    292:        if (nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)) {
1.32      rmind     293:                npf_recache(npc);
1.19      rmind     294:        }
                    295:        return ok;
1.1       rmind     296: }
                    297:
1.37.12.6! pgoyette  298: /*
        !           299:  * npf_set_mss: set the MSS.
        !           300:  */
        !           301: bool
        !           302: npf_set_mss(npf_cache_t *npc, uint16_t mss, uint16_t *old, uint16_t *new,
        !           303:     bool *mid)
        !           304: {
        !           305:        nbuf_t *nbuf = npc->npc_nbuf;
        !           306:        const struct tcphdr *th = npc->npc_l4.tcp;
        !           307:        int cnt, optlen = 0;
        !           308:        uint8_t *cp, *base, opt;
        !           309:        bool ok;
        !           310:
        !           311:        KASSERT(npf_iscached(npc, NPC_IP46));
        !           312:        KASSERT(npf_iscached(npc, NPC_TCP));
        !           313:
        !           314:        /* Determine if there are any TCP options, get their length. */
        !           315:        cnt = (th->th_off << 2) - sizeof(struct tcphdr);
        !           316:        if (cnt <= 0) {
        !           317:                /* No options. */
        !           318:                return false;
        !           319:        }
        !           320:        KASSERT(cnt <= MAX_TCPOPTLEN);
        !           321:
        !           322:        /* Fetch all the options at once. */
        !           323:        nbuf_reset(nbuf);
        !           324:        const int step = npc->npc_hlen + sizeof(struct tcphdr);
        !           325:        if ((base = nbuf_advance(nbuf, step, cnt)) == NULL) {
        !           326:                ok = false;
        !           327:                goto done;
        !           328:        }
        !           329:
        !           330:        /* Scan the options. */
        !           331:        for (cp = base; cnt > 0; cnt -= optlen, cp += optlen) {
        !           332:                opt = cp[0];
        !           333:                if (opt == TCPOPT_EOL)
        !           334:                        break;
        !           335:                if (opt == TCPOPT_NOP)
        !           336:                        optlen = 1;
        !           337:                else {
        !           338:                        if (cnt < 2)
        !           339:                                break;
        !           340:                        optlen = cp[1];
        !           341:                        if (optlen < 2 || optlen > cnt)
        !           342:                                break;
        !           343:                }
        !           344:
        !           345:                switch (opt) {
        !           346:                case TCPOPT_MAXSEG:
        !           347:                        if (optlen != TCPOLEN_MAXSEG)
        !           348:                                continue;
        !           349:                        if (((cp + 2) - base) % sizeof(uint16_t) != 0) {
        !           350:                                *mid = true;
        !           351:                                memcpy(&old[0], cp + 1, sizeof(uint16_t));
        !           352:                                memcpy(&old[1], cp + 3, sizeof(uint16_t));
        !           353:                                memcpy(cp + 2, &mss, sizeof(uint16_t));
        !           354:                                memcpy(&new[0], cp + 1, sizeof(uint16_t));
        !           355:                                memcpy(&new[1], cp + 3, sizeof(uint16_t));
        !           356:                        } else {
        !           357:                                *mid = false;
        !           358:                                memcpy(cp + 2, &mss, sizeof(uint16_t));
        !           359:                        }
        !           360:                        break;
        !           361:                default:
        !           362:                        break;
        !           363:                }
        !           364:        }
        !           365:
        !           366:        ok = true;
        !           367: done:
        !           368:        if (nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)) {
        !           369:                npf_recache(npc);
        !           370:        }
        !           371:        return ok;
        !           372: }
        !           373:
1.19      rmind     374: static int
                    375: npf_cache_ip(npf_cache_t *npc, nbuf_t *nbuf)
1.1       rmind     376: {
1.19      rmind     377:        const void *nptr = nbuf_dataptr(nbuf);
                    378:        const uint8_t ver = *(const uint8_t *)nptr;
                    379:        int flags = 0;
1.12      rmind     380:
1.37.12.2  pgoyette  381:        /*
                    382:         * We intentionally don't read the L4 payload after IPPROTO_AH.
                    383:         */
                    384:
1.4       rmind     385:        switch (ver >> 4) {
1.12      rmind     386:        case IPVERSION: {
1.19      rmind     387:                struct ip *ip;
1.12      rmind     388:
1.19      rmind     389:                ip = nbuf_ensure_contig(nbuf, sizeof(struct ip));
                    390:                if (ip == NULL) {
1.37.12.1  pgoyette  391:                        return NPC_FMTERR;
1.4       rmind     392:                }
1.12      rmind     393:
1.37.12.3  pgoyette  394:                /* Retrieve the complete header. */
1.10      rmind     395:                if ((u_int)(ip->ip_hl << 2) < sizeof(struct ip)) {
1.37.12.1  pgoyette  396:                        return NPC_FMTERR;
1.4       rmind     397:                }
1.37.12.3  pgoyette  398:                ip = nbuf_ensure_contig(nbuf, (u_int)(ip->ip_hl << 2));
                    399:                if (ip == NULL) {
                    400:                        return NPC_FMTERR;
                    401:                }
                    402:
1.4       rmind     403:                if (ip->ip_off & ~htons(IP_DF | IP_RF)) {
                    404:                        /* Note fragmentation. */
1.19      rmind     405:                        flags |= NPC_IPFRAG;
1.4       rmind     406:                }
1.12      rmind     407:
1.4       rmind     408:                /* Cache: layer 3 - IPv4. */
1.14      rmind     409:                npc->npc_alen = sizeof(struct in_addr);
1.28      rmind     410:                npc->npc_ips[NPF_SRC] = (npf_addr_t *)&ip->ip_src;
                    411:                npc->npc_ips[NPF_DST] = (npf_addr_t *)&ip->ip_dst;
1.7       zoltan    412:                npc->npc_hlen = ip->ip_hl << 2;
1.19      rmind     413:                npc->npc_proto = ip->ip_p;
                    414:
                    415:                npc->npc_ip.v4 = ip;
                    416:                flags |= NPC_IP4;
1.4       rmind     417:                break;
1.12      rmind     418:        }
1.4       rmind     419:
1.12      rmind     420:        case (IPV6_VERSION >> 4): {
1.19      rmind     421:                struct ip6_hdr *ip6;
                    422:                struct ip6_ext *ip6e;
1.37      christos  423:                struct ip6_frag *ip6f;
1.19      rmind     424:                size_t off, hlen;
1.37.12.1  pgoyette  425:                int frag_present;
1.19      rmind     426:
                    427:                ip6 = nbuf_ensure_contig(nbuf, sizeof(struct ip6_hdr));
                    428:                if (ip6 == NULL) {
1.37.12.1  pgoyette  429:                        return NPC_FMTERR;
1.7       zoltan    430:                }
1.19      rmind     431:
1.37.12.2  pgoyette  432:                /*
                    433:                 * XXX: We don't handle IPv6 Jumbograms.
                    434:                 */
                    435:
1.19      rmind     436:                /* Set initial next-protocol value. */
                    437:                hlen = sizeof(struct ip6_hdr);
                    438:                npc->npc_proto = ip6->ip6_nxt;
1.13      rmind     439:                npc->npc_hlen = hlen;
1.7       zoltan    440:
1.37.12.1  pgoyette  441:                frag_present = 0;
                    442:
1.12      rmind     443:                /*
1.19      rmind     444:                 * Advance by the length of the current header.
1.12      rmind     445:                 */
1.19      rmind     446:                off = nbuf_offset(nbuf);
1.37.12.1  pgoyette  447:                while ((ip6e = nbuf_advance(nbuf, hlen, sizeof(*ip6e))) != NULL) {
1.13      rmind     448:                        /*
                    449:                         * Determine whether we are going to continue.
                    450:                         */
1.19      rmind     451:                        switch (npc->npc_proto) {
1.13      rmind     452:                        case IPPROTO_HOPOPTS:
1.7       zoltan    453:                        case IPPROTO_DSTOPTS:
                    454:                        case IPPROTO_ROUTING:
1.19      rmind     455:                                hlen = (ip6e->ip6e_len + 1) << 3;
1.7       zoltan    456:                                break;
                    457:                        case IPPROTO_FRAGMENT:
1.37.12.1  pgoyette  458:                                if (frag_present++)
                    459:                                        return NPC_FMTERR;
1.37      christos  460:                                ip6f = nbuf_ensure_contig(nbuf, sizeof(*ip6f));
                    461:                                if (ip6f == NULL)
1.37.12.1  pgoyette  462:                                        return NPC_FMTERR;
                    463:
                    464:                                /* RFC6946: Skip dummy fragments. */
                    465:                                if (!ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK) &&
                    466:                                    !(ip6f->ip6f_offlg & IP6F_MORE_FRAG)) {
                    467:                                        hlen = sizeof(struct ip6_frag);
                    468:                                        break;
                    469:                                }
                    470:
                    471:                                hlen = 0;
                    472:                                flags |= NPC_IPFRAG;
1.37      christos  473:
1.7       zoltan    474:                                break;
                    475:                        default:
1.13      rmind     476:                                hlen = 0;
                    477:                                break;
                    478:                        }
                    479:
                    480:                        if (!hlen) {
1.7       zoltan    481:                                break;
                    482:                        }
1.19      rmind     483:                        npc->npc_proto = ip6e->ip6e_nxt;
1.13      rmind     484:                        npc->npc_hlen += hlen;
                    485:                }
1.7       zoltan    486:
1.37.12.3  pgoyette  487:                if (ip6e == NULL) {
                    488:                        return NPC_FMTERR;
                    489:                }
                    490:
1.23      rmind     491:                /*
                    492:                 * Re-fetch the header pointers (nbufs might have been
                    493:                 * reallocated).  Restore the original offset (if any).
                    494:                 */
1.19      rmind     495:                nbuf_reset(nbuf);
1.23      rmind     496:                ip6 = nbuf_dataptr(nbuf);
1.19      rmind     497:                if (off) {
                    498:                        nbuf_advance(nbuf, off, 0);
                    499:                }
                    500:
1.12      rmind     501:                /* Cache: layer 3 - IPv6. */
1.14      rmind     502:                npc->npc_alen = sizeof(struct in6_addr);
1.28      rmind     503:                npc->npc_ips[NPF_SRC] = (npf_addr_t *)&ip6->ip6_src;
1.37.12.2  pgoyette  504:                npc->npc_ips[NPF_DST] = (npf_addr_t *)&ip6->ip6_dst;
1.19      rmind     505:
                    506:                npc->npc_ip.v6 = ip6;
                    507:                flags |= NPC_IP6;
1.7       zoltan    508:                break;
1.12      rmind     509:        }
1.4       rmind     510:        default:
1.19      rmind     511:                break;
1.4       rmind     512:        }
1.19      rmind     513:        return flags;
1.1       rmind     514: }
                    515:
                    516: /*
1.4       rmind     517:  * npf_cache_all: general routine to cache all relevant IP (v4 or v6)
1.12      rmind     518:  * and TCP, UDP or ICMP headers.
1.19      rmind     519:  *
                    520:  * => nbuf offset shall be set accordingly.
1.1       rmind     521:  */
1.10      rmind     522: int
1.32      rmind     523: npf_cache_all(npf_cache_t *npc)
1.1       rmind     524: {
1.32      rmind     525:        nbuf_t *nbuf = npc->npc_nbuf;
1.19      rmind     526:        int flags, l4flags;
                    527:        u_int hlen;
                    528:
                    529:        /*
                    530:         * This routine is a main point where the references are cached,
                    531:         * therefore clear the flag as we reset.
                    532:         */
                    533: again:
                    534:        nbuf_unset_flag(nbuf, NBUF_DATAREF_RESET);
1.1       rmind     535:
1.19      rmind     536:        /*
                    537:         * First, cache the L3 header (IPv4 or IPv6).  If IP packet is
                    538:         * fragmented, then we cannot look into L4.
                    539:         */
                    540:        flags = npf_cache_ip(npc, nbuf);
1.37.12.1  pgoyette  541:        if ((flags & NPC_IP46) == 0 || (flags & NPC_IPFRAG) != 0 ||
                    542:            (flags & NPC_FMTERR) != 0) {
1.37.12.3  pgoyette  543:                goto out;
1.1       rmind     544:        }
1.19      rmind     545:        hlen = npc->npc_hlen;
                    546:
1.37.12.3  pgoyette  547:        /*
                    548:         * Note: we guarantee that the potential "Query Id" field of the
                    549:         * ICMPv4/ICMPv6 packets is in the nbuf. This field is used in the
                    550:         * ICMP ALG.
                    551:         */
1.19      rmind     552:        switch (npc->npc_proto) {
1.1       rmind     553:        case IPPROTO_TCP:
1.19      rmind     554:                /* Cache: layer 4 - TCP. */
                    555:                npc->npc_l4.tcp = nbuf_advance(nbuf, hlen,
                    556:                    sizeof(struct tcphdr));
                    557:                l4flags = NPC_LAYER4 | NPC_TCP;
1.10      rmind     558:                break;
1.1       rmind     559:        case IPPROTO_UDP:
1.19      rmind     560:                /* Cache: layer 4 - UDP. */
                    561:                npc->npc_l4.udp = nbuf_advance(nbuf, hlen,
                    562:                    sizeof(struct udphdr));
                    563:                l4flags = NPC_LAYER4 | NPC_UDP;
1.10      rmind     564:                break;
1.1       rmind     565:        case IPPROTO_ICMP:
1.19      rmind     566:                /* Cache: layer 4 - ICMPv4. */
                    567:                npc->npc_l4.icmp = nbuf_advance(nbuf, hlen,
1.37.12.3  pgoyette  568:                    ICMP_MINLEN);
1.19      rmind     569:                l4flags = NPC_LAYER4 | NPC_ICMP;
                    570:                break;
1.15      spz       571:        case IPPROTO_ICMPV6:
1.19      rmind     572:                /* Cache: layer 4 - ICMPv6. */
                    573:                npc->npc_l4.icmp6 = nbuf_advance(nbuf, hlen,
1.37.12.3  pgoyette  574:                    sizeof(struct icmp6_hdr));
1.19      rmind     575:                l4flags = NPC_LAYER4 | NPC_ICMP;
                    576:                break;
                    577:        default:
                    578:                l4flags = 0;
1.10      rmind     579:                break;
1.1       rmind     580:        }
1.19      rmind     581:
1.37.12.3  pgoyette  582:        /* Error out if nbuf_advance failed. */
                    583:        if (l4flags && npc->npc_l4.hdr == NULL) {
                    584:                goto err;
                    585:        }
                    586:
1.19      rmind     587:        if (nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)) {
                    588:                goto again;
                    589:        }
                    590:
1.37.12.3  pgoyette  591:        flags |= l4flags;
                    592:        npc->npc_info |= flags;
                    593:        return flags;
                    594:
                    595: err:
                    596:        flags = NPC_FMTERR;
                    597: out:
                    598:        nbuf_unset_flag(nbuf, NBUF_DATAREF_RESET);
1.19      rmind     599:        npc->npc_info |= flags;
                    600:        return flags;
                    601: }
                    602:
                    603: void
1.32      rmind     604: npf_recache(npf_cache_t *npc)
1.19      rmind     605: {
1.32      rmind     606:        nbuf_t *nbuf = npc->npc_nbuf;
1.24      martin    607:        const int mflags __diagused = npc->npc_info & (NPC_IP46 | NPC_LAYER4);
1.25      mrg       608:        int flags __diagused;
1.19      rmind     609:
                    610:        nbuf_reset(nbuf);
                    611:        npc->npc_info = 0;
1.32      rmind     612:        flags = npf_cache_all(npc);
                    613:
1.19      rmind     614:        KASSERT((flags & mflags) == mflags);
                    615:        KASSERT(nbuf_flag_p(nbuf, NBUF_DATAREF_RESET) == 0);
1.1       rmind     616: }
                    617:
                    618: /*
1.19      rmind     619:  * npf_rwrip: rewrite required IP address.
1.4       rmind     620:  */
                    621: bool
1.28      rmind     622: npf_rwrip(const npf_cache_t *npc, u_int which, const npf_addr_t *addr)
1.4       rmind     623: {
                    624:        KASSERT(npf_iscached(npc, NPC_IP46));
1.28      rmind     625:        KASSERT(which == NPF_SRC || which == NPF_DST);
1.4       rmind     626:
1.28      rmind     627:        memcpy(npc->npc_ips[which], addr, npc->npc_alen);
1.4       rmind     628:        return true;
                    629: }
                    630:
                    631: /*
1.19      rmind     632:  * npf_rwrport: rewrite required TCP/UDP port.
1.1       rmind     633:  */
                    634: bool
1.28      rmind     635: npf_rwrport(const npf_cache_t *npc, u_int which, const in_port_t port)
1.1       rmind     636: {
1.21      rmind     637:        const int proto = npc->npc_proto;
1.4       rmind     638:        in_port_t *oport;
1.1       rmind     639:
1.4       rmind     640:        KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
1.1       rmind     641:        KASSERT(proto == IPPROTO_TCP || proto == IPPROTO_UDP);
1.28      rmind     642:        KASSERT(which == NPF_SRC || which == NPF_DST);
1.1       rmind     643:
1.19      rmind     644:        /* Get the offset and store the port in it. */
1.4       rmind     645:        if (proto == IPPROTO_TCP) {
1.19      rmind     646:                struct tcphdr *th = npc->npc_l4.tcp;
1.28      rmind     647:                oport = (which == NPF_SRC) ? &th->th_sport : &th->th_dport;
1.1       rmind     648:        } else {
1.19      rmind     649:                struct udphdr *uh = npc->npc_l4.udp;
1.28      rmind     650:                oport = (which == NPF_SRC) ? &uh->uh_sport : &uh->uh_dport;
1.1       rmind     651:        }
1.19      rmind     652:        memcpy(oport, &port, sizeof(in_port_t));
1.1       rmind     653:        return true;
                    654: }
                    655:
                    656: /*
1.19      rmind     657:  * npf_rwrcksum: rewrite IPv4 and/or TCP/UDP checksum.
1.1       rmind     658:  */
                    659: bool
1.28      rmind     660: npf_rwrcksum(const npf_cache_t *npc, u_int which,
1.19      rmind     661:     const npf_addr_t *addr, const in_port_t port)
1.1       rmind     662: {
1.28      rmind     663:        const npf_addr_t *oaddr = npc->npc_ips[which];
1.21      rmind     664:        const int proto = npc->npc_proto;
1.19      rmind     665:        const int alen = npc->npc_alen;
1.18      rmind     666:        uint16_t *ocksum;
                    667:        in_port_t oport;
                    668:
1.19      rmind     669:        KASSERT(npf_iscached(npc, NPC_LAYER4));
1.28      rmind     670:        KASSERT(which == NPF_SRC || which == NPF_DST);
1.18      rmind     671:
1.4       rmind     672:        if (npf_iscached(npc, NPC_IP4)) {
1.19      rmind     673:                struct ip *ip = npc->npc_ip.v4;
                    674:                uint16_t ipsum = ip->ip_sum;
1.4       rmind     675:
1.19      rmind     676:                /* Recalculate IPv4 checksum and rewrite. */
                    677:                ip->ip_sum = npf_addr_cksum(ipsum, alen, oaddr, addr);
1.4       rmind     678:        } else {
                    679:                /* No checksum for IPv6. */
                    680:                KASSERT(npf_iscached(npc, NPC_IP6));
                    681:        }
                    682:
1.18      rmind     683:        /* Nothing else to do for ICMP. */
1.30      rmind     684:        if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) {
1.4       rmind     685:                return true;
                    686:        }
1.7       zoltan    687:        KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
1.4       rmind     688:
1.18      rmind     689:        /*
                    690:         * Calculate TCP/UDP checksum:
                    691:         * - Skip if UDP and the current checksum is zero.
                    692:         * - Fixup the IP address change.
                    693:         * - Fixup the port change, if required (non-zero).
                    694:         */
1.4       rmind     695:        if (proto == IPPROTO_TCP) {
1.19      rmind     696:                struct tcphdr *th = npc->npc_l4.tcp;
1.4       rmind     697:
1.18      rmind     698:                ocksum = &th->th_sum;
1.28      rmind     699:                oport = (which == NPF_SRC) ? th->th_sport : th->th_dport;
1.4       rmind     700:        } else {
1.19      rmind     701:                struct udphdr *uh = npc->npc_l4.udp;
1.4       rmind     702:
                    703:                KASSERT(proto == IPPROTO_UDP);
1.18      rmind     704:                ocksum = &uh->uh_sum;
                    705:                if (*ocksum == 0) {
1.4       rmind     706:                        /* No need to update. */
                    707:                        return true;
                    708:                }
1.28      rmind     709:                oport = (which == NPF_SRC) ? uh->uh_sport : uh->uh_dport;
1.18      rmind     710:        }
                    711:
1.19      rmind     712:        uint16_t cksum = npf_addr_cksum(*ocksum, alen, oaddr, addr);
1.18      rmind     713:        if (port) {
                    714:                cksum = npf_fixup16_cksum(cksum, oport, port);
1.4       rmind     715:        }
1.1       rmind     716:
1.19      rmind     717:        /* Rewrite TCP/UDP checksum. */
                    718:        memcpy(ocksum, &cksum, sizeof(uint16_t));
1.4       rmind     719:        return true;
                    720: }
                    721:
1.29      rmind     722: /*
1.30      rmind     723:  * npf_napt_rwr: perform address and/or port translation.
                    724:  */
                    725: int
                    726: npf_napt_rwr(const npf_cache_t *npc, u_int which,
                    727:     const npf_addr_t *addr, const in_addr_t port)
                    728: {
                    729:        const unsigned proto = npc->npc_proto;
                    730:
                    731:        /*
                    732:         * Rewrite IP and/or TCP/UDP checksums first, since we need the
                    733:         * current (old) address/port for the calculations.  Then perform
                    734:         * the address translation i.e. rewrite source or destination.
                    735:         */
                    736:        if (!npf_rwrcksum(npc, which, addr, port)) {
                    737:                return EINVAL;
                    738:        }
                    739:        if (!npf_rwrip(npc, which, addr)) {
                    740:                return EINVAL;
                    741:        }
                    742:        if (port == 0) {
                    743:                /* Done. */
                    744:                return 0;
                    745:        }
                    746:
                    747:        switch (proto) {
                    748:        case IPPROTO_TCP:
                    749:        case IPPROTO_UDP:
                    750:                /* Rewrite source/destination port. */
                    751:                if (!npf_rwrport(npc, which, port)) {
                    752:                        return EINVAL;
                    753:                }
                    754:                break;
                    755:        case IPPROTO_ICMP:
                    756:        case IPPROTO_ICMPV6:
                    757:                KASSERT(npf_iscached(npc, NPC_ICMP));
                    758:                /* Nothing. */
                    759:                break;
                    760:        default:
                    761:                return ENOTSUP;
                    762:        }
                    763:        return 0;
                    764: }
                    765:
                    766: /*
1.29      rmind     767:  * IPv6-to-IPv6 Network Prefix Translation (NPTv6), as per RFC 6296.
                    768:  */
                    769:
                    770: int
                    771: npf_npt66_rwr(const npf_cache_t *npc, u_int which, const npf_addr_t *pref,
                    772:     npf_netmask_t len, uint16_t adj)
                    773: {
                    774:        npf_addr_t *addr = npc->npc_ips[which];
                    775:        unsigned remnant, word, preflen = len >> 4;
                    776:        uint32_t sum;
                    777:
                    778:        KASSERT(which == NPF_SRC || which == NPF_DST);
                    779:
                    780:        if (!npf_iscached(npc, NPC_IP6)) {
                    781:                return EINVAL;
                    782:        }
                    783:        if (len <= 48) {
                    784:                /*
                    785:                 * The word to adjust.  Cannot translate the 0xffff
                    786:                 * subnet if /48 or shorter.
                    787:                 */
                    788:                word = 3;
1.36      christos  789:                if (addr->word16[word] == 0xffff) {
1.29      rmind     790:                        return EINVAL;
                    791:                }
                    792:        } else {
                    793:                /*
                    794:                 * Also, all 0s or 1s in the host part are disallowed for
                    795:                 * longer than /48 prefixes.
                    796:                 */
1.36      christos  797:                if ((addr->word32[2] == 0 && addr->word32[3] == 0) ||
                    798:                    (addr->word32[2] == ~0U && addr->word32[3] == ~0U))
1.29      rmind     799:                        return EINVAL;
                    800:
                    801:                /* Determine the 16-bit word to adjust. */
                    802:                for (word = 4; word < 8; word++)
1.36      christos  803:                        if (addr->word16[word] != 0xffff)
1.29      rmind     804:                                break;
                    805:        }
                    806:
                    807:        /* Rewrite the prefix. */
                    808:        for (unsigned i = 0; i < preflen; i++) {
1.36      christos  809:                addr->word16[i] = pref->word16[i];
1.29      rmind     810:        }
                    811:
                    812:        /*
                    813:         * If prefix length is within a 16-bit word (not dividable by 16),
                    814:         * then prepare a mask, determine the word and adjust it.
                    815:         */
                    816:        if ((remnant = len - (preflen << 4)) != 0) {
                    817:                const uint16_t wordmask = (1U << remnant) - 1;
                    818:                const unsigned i = preflen;
                    819:
1.36      christos  820:                addr->word16[i] = (pref->word16[i] & wordmask) |
                    821:                    (addr->word16[i] & ~wordmask);
1.29      rmind     822:        }
                    823:
                    824:        /*
                    825:         * Performing 1's complement sum/difference.
                    826:         */
1.36      christos  827:        sum = addr->word16[word] + adj;
1.29      rmind     828:        while (sum >> 16) {
                    829:                sum = (sum >> 16) + (sum & 0xffff);
                    830:        }
                    831:        if (sum == 0xffff) {
                    832:                /* RFC 1071. */
                    833:                sum = 0x0000;
                    834:        }
1.36      christos  835:        addr->word16[word] = sum;
1.29      rmind     836:        return 0;
                    837: }
                    838:
1.13      rmind     839: #if defined(DDB) || defined(_NPF_TESTING)
                    840:
1.31      rmind     841: const char *
                    842: npf_addr_dump(const npf_addr_t *addr, int alen)
1.13      rmind     843: {
1.31      rmind     844:        if (alen == sizeof(struct in_addr)) {
                    845:                struct in_addr ip;
                    846:                memcpy(&ip, addr, alen);
                    847:                return inet_ntoa(ip);
                    848:        }
1.36      christos  849:        return "[IPv6]";
1.13      rmind     850: }
                    851:
                    852: #endif

CVSweb <webmaster@jp.NetBSD.org>