Home | History | Annotate | Line # | Download | only in npf
      1 /*-
      2  * Copyright (c) 2025 Emmanuel Nyarko
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     14  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     15  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     23  * POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #ifdef _KERNEL
     27 #include <sys/cdefs.h>
     28 __KERNEL_RCSID(0, "$NetBSD: npf_socket.c,v 1.4 2025/10/03 22:42:58 joe Exp $");
     29 
     30 #include <sys/param.h>
     31 #include <sys/types.h>
     32 
     33 #include <netinet/tcp.h>
     34 #include <netinet/udp.h>
     35 #include <netinet/in_pcb.h>
     36 #include <sys/socketvar.h>
     37 
     38 #include <netinet/ip6.h>
     39 #include <netinet6/ip6_var.h>
     40 #include <netinet6/in6_pcb.h>
     41 #endif
     42 
     43 #include "npf_impl.h"
     44 
     45 extern	struct inpcbtable tcbtable;	/* head of queue of active tcpcb's */
     46 extern	struct inpcbtable udbtable;
     47 
     48 #if defined(INET6)
     49 static struct socket *	npf_ip6_socket(npf_cache_t *, int);
     50 #endif
     51 static struct socket *	npf_ip_socket(npf_cache_t *, int);
     52 static int		npf_match(uint8_t, uint32_t, uint32_t, uint32_t);
     53 
     54 /*
     55 * NPF process socket module
     56 */
     57 
     58 int
     59 npf_match_rid(rid_t *rid, uint32_t uid_lookup)
     60 {
     61     return npf_match(rid->op, rid->id[0], rid->id[1], uid_lookup);
     62 }
     63 
     64 static int
     65 npf_match(uint8_t op, uint32_t rid1, uint32_t rid2, uint32_t id_lp)
     66 {
     67     switch (op) {
     68     case NPF_OP_IRG:
     69         return id_lp > rid1 && id_lp < rid2;
     70     case NPF_OP_XRG:
     71         return id_lp < rid1 || id_lp > rid2;
     72     case NPF_OP_EQ:
     73         return id_lp == rid1;
     74     case NPF_OP_NE:
     75         return id_lp != rid1;
     76     case NPF_OP_LT:
     77         return id_lp < rid1;
     78     case NPF_OP_LE:
     79         return id_lp <= rid1;
     80     case NPF_OP_GT:
     81         return id_lp > rid1;
     82     case NPF_OP_GE:
     83         return id_lp >= rid1;
     84     }
     85     return 0; /* never reached */
     86 }
     87 
     88 int
     89 npf_socket_lookup_rid(npf_cache_t *npc, get_rid_t get_rid, uint32_t *rid, int dir)
     90 {
     91     struct socket	*so = NULL;
     92 
     93     KASSERT(npf_iscached(npc, NPC_IP46));
     94 
     95     mutex_enter(softnet_lock);
     96     if (npf_iscached(npc, NPC_IP4)) {
     97         so = npf_ip_socket(npc, dir);
     98 #if defined(INET6)
     99     } else if (npf_iscached(npc, NPC_IP6)) {
    100         so = npf_ip6_socket(npc, dir);
    101 #endif
    102     }
    103 
    104     if (so == NULL || so->so_cred == NULL)
    105         return -1;
    106 
    107     *rid = get_rid(so->so_cred);
    108     mutex_exit(softnet_lock);
    109 
    110     return 0;
    111 }
    112 
    113 static struct socket *
    114 npf_ip_socket(npf_cache_t *npc, int dir)
    115 {
    116     struct inpcbtable	*tb = NULL;
    117     struct in_addr	saddr, daddr;
    118     uint16_t		sport, dport;
    119     struct socket		*so = NULL;
    120     struct inpcb		*inp = NULL;
    121 
    122 #define in_pcbhashlookup(tbl, saddr, sport, daddr, dport) \
    123     inpcb_lookup(tbl, saddr, sport, daddr, dport, NULL)
    124 #define in_pcblookup_listen(tbl, addr, port) \
    125     inpcb_lookup_bound(tbl, addr, port)
    126 
    127     KASSERT(npf_iscached(npc, NPC_LAYER4));
    128     KASSERT(npf_iscached(npc, NPC_IP4));
    129 
    130     struct tcphdr *tcp = npc->npc_l4.tcp;
    131     struct udphdr *udp = npc->npc_l4.udp;
    132     struct ip *ip = npc->npc_ip.v4;
    133 
    134     switch(npc->npc_proto) {
    135         case IPPROTO_TCP:
    136             sport = tcp->th_sport;
    137             dport = tcp->th_dport;
    138             tb = &tcbtable;
    139             break;
    140         case IPPROTO_UDP:
    141             sport = udp->uh_sport;
    142             dport = udp->uh_dport;
    143             tb = &udbtable;
    144             break;
    145         default:
    146             return NULL;
    147     }
    148 
    149     if (dir == PFIL_IN) {
    150         saddr = ip->ip_src;
    151         daddr = ip->ip_dst;
    152     } else {
    153         uint16_t p_temp;
    154         /* swap ports and addresses */
    155         p_temp = sport;
    156         sport = dport;
    157         dport = p_temp;
    158         saddr = ip->ip_dst;
    159         daddr = ip->ip_src;
    160     }
    161 
    162     inp = in_pcbhashlookup(tb, saddr, sport, daddr, dport);
    163     if (inp == NULL) {
    164         inp = in_pcblookup_listen(tb, daddr, dport);
    165         if (inp == NULL) {
    166             return NULL;
    167         }
    168     }
    169 
    170     so = inp->inp_socket;
    171     return so;
    172 }
    173 
    174 #if defined(INET6)
    175 static struct socket *
    176 npf_ip6_socket(npf_cache_t *npc, int dir)
    177 {
    178     struct inpcbtable	*tb = NULL;
    179     const struct in6_addr	*s6addr, *d6addr;
    180     uint16_t	sport, dport;
    181     struct inpcb		*in6p = NULL;
    182     struct socket		*so = NULL;
    183 
    184 #define in6_pcbhashlookup(tbl, saddr, sport, daddr, dport) \
    185     in6pcb_lookup(tbl, saddr, sport, daddr, dport, 0, NULL)
    186 
    187 #define in6_pcblookup_listen(tbl, addr, port) \
    188     in6pcb_lookup_bound(tbl, addr, port, 0)
    189 
    190     KASSERT(npf_iscached(npc, NPC_LAYER4));
    191     KASSERT(npf_iscached(npc, NPC_IP6));
    192 
    193     struct tcphdr *tcp = npc->npc_l4.tcp;
    194     struct udphdr *udp = npc->npc_l4.udp;
    195     struct ip6_hdr *ip6 = npc->npc_ip.v6;
    196 
    197     switch(npc->npc_proto) {
    198         case IPPROTO_TCP:
    199             sport = tcp->th_sport;
    200             dport = tcp->th_dport;
    201             tb = &tcbtable;
    202             break;
    203         case IPPROTO_UDP:
    204             sport = udp->uh_sport;
    205             dport = udp->uh_dport;
    206             tb = &udbtable;
    207             break;
    208         default:
    209             return NULL;
    210     }
    211 
    212     if (dir == PFIL_IN) {
    213         s6addr = &ip6->ip6_src;
    214         d6addr = &ip6->ip6_dst;
    215     } else {
    216         uint16_t p_temp;
    217         /* swap ports and addresses */
    218         p_temp = sport;
    219         sport = dport;
    220         dport = p_temp;
    221         s6addr = &ip6->ip6_dst;
    222         d6addr = &ip6->ip6_src;
    223     }
    224     in6p = in6_pcbhashlookup(tb, s6addr, sport, d6addr,
    225         dport);
    226     if (in6p == NULL) {
    227         in6p = in6_pcblookup_listen(tb, d6addr, dport);
    228         if (in6p == NULL)
    229             return NULL;
    230     }
    231 
    232     so = in6p->inp_socket;
    233     return so;
    234 }
    235 #endif
    236