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