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