Home | History | Annotate | Line # | Download | only in npf
npf_socket.c revision 1.1
      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 #include <netinet/tcp.h>
     27 #include <netinet/udp.h>
     28 #include <netinet/in_pcb.h>
     29 #include <sys/socketvar.h>
     30 
     31 #ifdef INET6
     32 #include <netinet/ip6.h>
     33 #include <netinet6/ip6_var.h>
     34 #ifdef __NetBSD__
     35 #include <netinet6/in6_pcb.h>
     36 #endif /* __NetBSD__ */
     37 #endif /* INET6 */
     38 
     39 #include "npf_impl.h"
     40 
     41 extern	struct inpcbtable tcbtable;	/* head of queue of active tcpcb's */
     42 extern	struct inpcbtable udbtable;
     43 
     44 static struct socket *	npf_ip6_socket(npf_cache_t *, int);
     45 static struct socket *	npf_ip_socket(npf_cache_t *, int);
     46 static int		npf_match(uint8_t, uint32_t, uint32_t, uint32_t);
     47 
     48 /*
     49 * NPF process socket module
     50 */
     51 
     52 int
     53 npf_match_rid(rid_t *rid, uint32_t uid_lookup)
     54 {
     55     return npf_match(rid->op, rid->id[0], rid->id[1], uid_lookup);
     56 }
     57 
     58 static int
     59 npf_match(uint8_t op, uint32_t rid1, uint32_t rid2, uint32_t id_lp)
     60 {
     61     switch (op) {
     62     case NPF_OP_IRG:
     63         return id_lp > rid1 && id_lp < rid2;
     64     case NPF_OP_XRG:
     65         return id_lp < rid1 || id_lp > rid2;
     66     case NPF_OP_EQ:
     67         return id_lp == rid1;
     68     case NPF_OP_NE:
     69         return id_lp != rid1;
     70     case NPF_OP_LT:
     71         return id_lp < rid1;
     72     case NPF_OP_LE:
     73         return id_lp <= rid1;
     74     case NPF_OP_GT:
     75         return id_lp > rid1;
     76     case NPF_OP_GE:
     77         return id_lp >= rid1;
     78     }
     79     return 0; /* never reached */
     80 }
     81 
     82 int
     83 npf_socket_lookup_rid(npf_cache_t *npc, get_rid_t get_rid, uint32_t *rid, int dir)
     84 {
     85     struct socket	*so = NULL;
     86 
     87     KASSERT(npf_iscached(npc, NPC_IP46));
     88 
     89     if (npf_iscached(npc, NPC_IP4)) {
     90         so = npf_ip_socket(npc, dir);
     91     } else if (npf_iscached(npc, NPC_IP6)) {
     92         so = npf_ip6_socket(npc, dir);
     93     }
     94 
     95     if (so == NULL || so->so_cred == NULL)
     96         return -1;
     97 
     98     *rid = get_rid(so->so_cred);
     99     return 0;
    100 }
    101 
    102 static struct socket *
    103 npf_ip_socket(npf_cache_t *npc, int dir)
    104 {
    105     struct inpcbtable	*tb = NULL;
    106     struct in_addr	saddr, daddr;
    107     uint16_t		sport, dport;
    108     struct socket		*so = NULL;
    109     struct inpcb		*inp = NULL;
    110 
    111 #define in_pcbhashlookup(tbl, saddr, sport, daddr, dport) \
    112     inpcb_lookup(tbl, saddr, sport, daddr, dport, NULL)
    113 #define in_pcblookup_listen(tbl, addr, port) \
    114     inpcb_lookup_bound(tbl, addr, port)
    115 
    116     KASSERT(npf_iscached(npc, NPC_LAYER4));
    117     KASSERT(npf_iscached(npc, NPC_IP4));
    118 
    119     struct tcphdr *tcp = npc->npc_l4.tcp;
    120     struct udphdr *udp = npc->npc_l4.udp;
    121     struct ip *ip = npc->npc_ip.v4;
    122 
    123     switch(npc->npc_proto) {
    124         case IPPROTO_TCP:
    125             sport = tcp->th_sport;
    126             dport = tcp->th_dport;
    127             tb = &tcbtable;
    128             break;
    129         case IPPROTO_UDP:
    130             sport = udp->uh_sport;
    131             dport = udp->uh_dport;
    132             tb = &udbtable;
    133             break;
    134         default:
    135             return NULL;
    136     }
    137 
    138     if (dir == PFIL_IN) {
    139         saddr = ip->ip_src;
    140         daddr = ip->ip_dst;
    141     } else {
    142         uint16_t p_temp;
    143         /* swap ports and addresses */
    144         p_temp = sport;
    145         sport = dport;
    146         dport = p_temp;
    147         saddr = ip->ip_dst;
    148         daddr = ip->ip_src;
    149     }
    150 
    151     inp = in_pcbhashlookup(tb, saddr, sport, daddr, dport);
    152     if (inp == NULL) {
    153         inp = in_pcblookup_listen(tb, daddr, dport);
    154         if (inp == NULL) {
    155             return NULL;
    156         }
    157     }
    158 
    159     so = inp->inp_socket;
    160     return so;
    161 }
    162 
    163 static struct socket *
    164 npf_ip6_socket(npf_cache_t *npc, int dir)
    165 {
    166     struct inpcbtable	*tb = NULL;
    167     const struct in6_addr	*s6addr, *d6addr;
    168     uint16_t	sport, dport;
    169     struct inpcb		*in6p = NULL;
    170     struct socket		*so = NULL;
    171 
    172 #define in6_pcbhashlookup(tbl, saddr, sport, daddr, dport) \
    173     in6pcb_lookup(tbl, saddr, sport, daddr, dport, 0, NULL)
    174 
    175 #define in6_pcblookup_listen(tbl, addr, port) \
    176     in6pcb_lookup_bound(tbl, addr, port, 0)
    177 
    178     KASSERT(npf_iscached(npc, NPC_LAYER4));
    179     KASSERT(npf_iscached(npc, NPC_IP6));
    180 
    181     struct tcphdr *tcp = npc->npc_l4.tcp;
    182     struct udphdr *udp = npc->npc_l4.udp;
    183     struct ip6_hdr *ip6 = npc->npc_ip.v6;
    184 
    185     switch(npc->npc_proto) {
    186         case IPPROTO_TCP:
    187             sport = tcp->th_sport;
    188             dport = tcp->th_dport;
    189             tb = &tcbtable;
    190             break;
    191         case IPPROTO_UDP:
    192             sport = udp->uh_sport;
    193             dport = udp->uh_dport;
    194             tb = &udbtable;
    195             break;
    196         default:
    197             return NULL;
    198     }
    199 
    200     if (dir == PFIL_IN) {
    201         s6addr = &ip6->ip6_src;
    202         d6addr = &ip6->ip6_dst;
    203     } else {
    204         uint16_t p_temp;
    205         /* swap ports and addresses */
    206         p_temp = sport;
    207         sport = dport;
    208         dport = p_temp;
    209         s6addr = &ip6->ip6_dst;
    210         d6addr = &ip6->ip6_src;
    211     }
    212     in6p = in6_pcbhashlookup(tb, s6addr, sport, d6addr,
    213         dport);
    214     if (in6p == NULL) {
    215         in6p = in6_pcblookup_listen(tb, d6addr, dport);
    216         if (in6p == NULL)
    217             return NULL;
    218     }
    219     so = in6p->inp_socket;
    220     return so;
    221 }
    222