1 /*- 2 * Copyright (c) 2010-2011 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This material is based upon work partially supported by The 6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * NPF module for packet construction routines. 32 */ 33 34 #ifdef _KERNEL 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: npf_sendpkt.c,v 1.22 2020/05/30 14:16:56 rmind Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/types.h> 40 41 #include <netinet/in_systm.h> 42 #include <netinet/in.h> 43 #include <netinet/ip.h> 44 #include <netinet/ip_icmp.h> 45 #include <netinet/ip_var.h> 46 #include <netinet/tcp.h> 47 #include <netinet/ip6.h> 48 #include <netinet/icmp6.h> 49 #include <netinet6/ip6_var.h> 50 #include <netinet6/scope6_var.h> 51 #include <sys/mbuf.h> 52 #endif 53 54 #include "npf_impl.h" 55 56 #define DEFAULT_IP_TTL (ip_defttl) 57 58 #if defined(_NPF_STANDALONE) 59 #define m_gethdr(t, f) (npf)->mbufops->alloc((npf), 0, 0) 60 #define m_freem(m) (npc)->npc_ctx->mbufops->free(m) 61 #define mtod(m,t) ((t)((npc)->npc_ctx->mbufops->getdata(m))) 62 #endif 63 64 #if !defined(INET6) || defined(_NPF_STANDALONE) 65 #define in6_cksum(...) 0 66 #define ip6_output(...) 0 67 #define icmp6_error(m, ...) m_freem(m) 68 #define npf_ip6_setscope(n, i) ((void)(i), 0) 69 #endif 70 71 #if defined(INET6) 72 static int 73 npf_ip6_setscope(const npf_cache_t *npc, struct ip6_hdr *ip6) 74 { 75 const struct ifnet *rcvif = npc->npc_nbuf->nb_ifp; 76 77 if (in6_clearscope(&ip6->ip6_src) || in6_clearscope(&ip6->ip6_dst)) { 78 return EINVAL; 79 } 80 if (in6_setscope(&ip6->ip6_src, rcvif, NULL) || 81 in6_setscope(&ip6->ip6_dst, rcvif, NULL)) { 82 return EINVAL; 83 } 84 return 0; 85 } 86 #endif 87 88 /* 89 * npf_return_tcp: return a TCP reset (RST) packet. 90 */ 91 static int 92 npf_return_tcp(npf_cache_t *npc) 93 { 94 npf_t *npf = npc->npc_ctx; 95 struct mbuf *m; 96 struct ip *ip = NULL; 97 struct ip6_hdr *ip6 = NULL; 98 struct tcphdr *oth, *th; 99 tcp_seq seq, ack; 100 int tcpdlen, len; 101 uint32_t win; 102 103 /* Fetch relevant data. */ 104 KASSERT(npf_iscached(npc, NPC_IP46)); 105 KASSERT(npf_iscached(npc, NPC_LAYER4)); 106 tcpdlen = npf_tcpsaw(npc, &seq, &ack, &win); 107 oth = npc->npc_l4.tcp; 108 109 if (oth->th_flags & TH_RST) { 110 return 0; 111 } 112 113 /* Create and setup a network buffer. */ 114 if (npf_iscached(npc, NPC_IP4)) { 115 len = sizeof(struct ip) + sizeof(struct tcphdr); 116 } else if (npf_iscached(npc, NPC_IP6)) { 117 len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr); 118 } else { 119 return EINVAL; 120 } 121 122 m = m_gethdr(M_DONTWAIT, MT_HEADER); 123 if (m == NULL) { 124 return ENOMEM; 125 } 126 #if !defined(_NPF_STANDALONE) 127 m->m_data += max_linkhdr; 128 m->m_len = len; 129 m->m_pkthdr.len = len; 130 (void)npf; 131 #endif 132 if (npf_iscached(npc, NPC_IP4)) { 133 struct ip *oip = npc->npc_ip.v4; 134 135 ip = mtod(m, struct ip *); 136 memset(ip, 0, len); 137 138 /* 139 * First, partially fill IPv4 header for TCP checksum. 140 * Note: IP length contains TCP header length. 141 */ 142 ip->ip_p = IPPROTO_TCP; 143 ip->ip_src.s_addr = oip->ip_dst.s_addr; 144 ip->ip_dst.s_addr = oip->ip_src.s_addr; 145 ip->ip_len = htons(sizeof(struct tcphdr)); 146 147 th = (struct tcphdr *)(ip + 1); 148 } else { 149 struct ip6_hdr *oip = npc->npc_ip.v6; 150 151 KASSERT(npf_iscached(npc, NPC_IP6)); 152 ip6 = mtod(m, struct ip6_hdr *); 153 memset(ip6, 0, len); 154 155 ip6->ip6_nxt = IPPROTO_TCP; 156 ip6->ip6_hlim = IPV6_DEFHLIM; 157 memcpy(&ip6->ip6_src, &oip->ip6_dst, sizeof(struct in6_addr)); 158 memcpy(&ip6->ip6_dst, &oip->ip6_src, sizeof(struct in6_addr)); 159 ip6->ip6_plen = htons(len); 160 ip6->ip6_vfc = IPV6_VERSION; 161 162 th = (struct tcphdr *)(ip6 + 1); 163 } 164 165 /* 166 * Construct TCP header and compute the checksum. 167 */ 168 th->th_sport = oth->th_dport; 169 th->th_dport = oth->th_sport; 170 th->th_seq = htonl(ack); 171 if (oth->th_flags & TH_SYN) { 172 tcpdlen++; 173 } 174 th->th_ack = htonl(seq + tcpdlen); 175 th->th_off = sizeof(struct tcphdr) >> 2; 176 th->th_flags = TH_ACK | TH_RST; 177 178 if (npf_iscached(npc, NPC_IP4)) { 179 th->th_sum = in_cksum(m, len); 180 181 /* 182 * Second, fill the rest of IPv4 header and correct IP length. 183 */ 184 ip->ip_v = IPVERSION; 185 ip->ip_hl = sizeof(struct ip) >> 2; 186 ip->ip_tos = IPTOS_LOWDELAY; 187 ip->ip_len = htons(len); 188 ip->ip_ttl = DEFAULT_IP_TTL; 189 } else { 190 KASSERT(npf_iscached(npc, NPC_IP6)); 191 th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr), 192 sizeof(struct tcphdr)); 193 194 /* Handle IPv6 scopes */ 195 if (npf_ip6_setscope(npc, ip6) != 0) { 196 goto bad; 197 } 198 } 199 200 /* Pass to IP layer. */ 201 if (npf_iscached(npc, NPC_IP4)) { 202 return ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); 203 } 204 return ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL); 205 bad: 206 m_freem(m); 207 return EINVAL; 208 } 209 210 /* 211 * npf_return_icmp: return an ICMP error. 212 */ 213 static int 214 npf_return_icmp(const npf_cache_t *npc) 215 { 216 struct mbuf *m = nbuf_head_mbuf(npc->npc_nbuf); 217 218 if (npf_iscached(npc, NPC_IP4)) { 219 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_ADMIN_PROHIBIT, 0, 0); 220 return 0; 221 } else if (npf_iscached(npc, NPC_IP6)) { 222 /* Handle IPv6 scopes */ 223 struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 224 225 if (npf_ip6_setscope(npc, ip6) != 0) { 226 return EINVAL; 227 } 228 icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN, 0); 229 return 0; 230 } 231 return EINVAL; 232 } 233 234 /* 235 * npf_return_block: return TCP reset or ICMP host unreachable packet. 236 * 237 * => Returns true if the buffer was consumed (freed) and false otherwise. 238 */ 239 bool 240 npf_return_block(npf_cache_t *npc, const int retfl) 241 { 242 if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) { 243 return false; 244 } 245 switch (npc->npc_proto) { 246 case IPPROTO_TCP: 247 if (retfl & NPF_RULE_RETRST) { 248 (void)npf_return_tcp(npc); 249 } 250 break; 251 case IPPROTO_UDP: 252 if (retfl & NPF_RULE_RETICMP) 253 if (npf_return_icmp(npc) == 0) 254 return true; 255 break; 256 } 257 return false; 258 } 259