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.3 2025/06/02 13:19:27 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     if (npf_iscached(npc, NPC_IP4)) {
     96         so = npf_ip_socket(npc, dir);
     97 #if defined(INET6)
     98     } else if (npf_iscached(npc, NPC_IP6)) {
     99         so = npf_ip6_socket(npc, dir);
    100 #endif
    101     }
    102 
    103     if (so == NULL || so->so_cred == NULL)
    104         return -1;
    105 
    106     *rid = get_rid(so->so_cred);
    107     return 0;
    108 }
    109 
    110 static struct socket *
    111 npf_ip_socket(npf_cache_t *npc, int dir)
    112 {
    113     struct inpcbtable	*tb = NULL;
    114     struct in_addr	saddr, daddr;
    115     uint16_t		sport, dport;
    116     struct socket		*so = NULL;
    117     struct inpcb		*inp = NULL;
    118 
    119 #define in_pcbhashlookup(tbl, saddr, sport, daddr, dport) \
    120     inpcb_lookup(tbl, saddr, sport, daddr, dport, NULL)
    121 #define in_pcblookup_listen(tbl, addr, port) \
    122     inpcb_lookup_bound(tbl, addr, port)
    123 
    124     KASSERT(npf_iscached(npc, NPC_LAYER4));
    125     KASSERT(npf_iscached(npc, NPC_IP4));
    126 
    127     struct tcphdr *tcp = npc->npc_l4.tcp;
    128     struct udphdr *udp = npc->npc_l4.udp;
    129     struct ip *ip = npc->npc_ip.v4;
    130 
    131     switch(npc->npc_proto) {
    132         case IPPROTO_TCP:
    133             sport = tcp->th_sport;
    134             dport = tcp->th_dport;
    135             tb = &tcbtable;
    136             break;
    137         case IPPROTO_UDP:
    138             sport = udp->uh_sport;
    139             dport = udp->uh_dport;
    140             tb = &udbtable;
    141             break;
    142         default:
    143             return NULL;
    144     }
    145 
    146     if (dir == PFIL_IN) {
    147         saddr = ip->ip_src;
    148         daddr = ip->ip_dst;
    149     } else {
    150         uint16_t p_temp;
    151         /* swap ports and addresses */
    152         p_temp = sport;
    153         sport = dport;
    154         dport = p_temp;
    155         saddr = ip->ip_dst;
    156         daddr = ip->ip_src;
    157     }
    158 
    159     inp = in_pcbhashlookup(tb, saddr, sport, daddr, dport);
    160     if (inp == NULL) {
    161         inp = in_pcblookup_listen(tb, daddr, dport);
    162         if (inp == NULL) {
    163             return NULL;
    164         }
    165     }
    166 
    167     so = inp->inp_socket;
    168     return so;
    169 }
    170 
    171 #if defined(INET6)
    172 static struct socket *
    173 npf_ip6_socket(npf_cache_t *npc, int dir)
    174 {
    175     struct inpcbtable	*tb = NULL;
    176     const struct in6_addr	*s6addr, *d6addr;
    177     uint16_t	sport, dport;
    178     struct inpcb		*in6p = NULL;
    179     struct socket		*so = NULL;
    180 
    181 #define in6_pcbhashlookup(tbl, saddr, sport, daddr, dport) \
    182     in6pcb_lookup(tbl, saddr, sport, daddr, dport, 0, NULL)
    183 
    184 #define in6_pcblookup_listen(tbl, addr, port) \
    185     in6pcb_lookup_bound(tbl, addr, port, 0)
    186 
    187     KASSERT(npf_iscached(npc, NPC_LAYER4));
    188     KASSERT(npf_iscached(npc, NPC_IP6));
    189 
    190     struct tcphdr *tcp = npc->npc_l4.tcp;
    191     struct udphdr *udp = npc->npc_l4.udp;
    192     struct ip6_hdr *ip6 = npc->npc_ip.v6;
    193 
    194     switch(npc->npc_proto) {
    195         case IPPROTO_TCP:
    196             sport = tcp->th_sport;
    197             dport = tcp->th_dport;
    198             tb = &tcbtable;
    199             break;
    200         case IPPROTO_UDP:
    201             sport = udp->uh_sport;
    202             dport = udp->uh_dport;
    203             tb = &udbtable;
    204             break;
    205         default:
    206             return NULL;
    207     }
    208 
    209     if (dir == PFIL_IN) {
    210         s6addr = &ip6->ip6_src;
    211         d6addr = &ip6->ip6_dst;
    212     } else {
    213         uint16_t p_temp;
    214         /* swap ports and addresses */
    215         p_temp = sport;
    216         sport = dport;
    217         dport = p_temp;
    218         s6addr = &ip6->ip6_dst;
    219         d6addr = &ip6->ip6_src;
    220     }
    221     in6p = in6_pcbhashlookup(tb, s6addr, sport, d6addr,
    222         dport);
    223     if (in6p == NULL) {
    224         in6p = in6_pcblookup_listen(tb, d6addr, dport);
    225         if (in6p == NULL)
    226             return NULL;
    227     }
    228     so = in6p->inp_socket;
    229     return so;
    230 }
    231 #endif
    232