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