Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: move nf_send_resetX() code to nf_reject_ipvX modules

Move nf_send_reset() and nf_send_reset6() to nf_reject_ipv4 and
nf_reject_ipv6 respectively. This code is shared by x_tables and
nf_tables.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+309 -117
+1 -117
include/net/netfilter/ipv4/nf_reject.h
··· 1 1 #ifndef _IPV4_NF_REJECT_H 2 2 #define _IPV4_NF_REJECT_H 3 3 4 - #include <net/ip.h> 5 - #include <net/tcp.h> 6 - #include <net/route.h> 7 - #include <net/dst.h> 8 4 #include <net/icmp.h> 9 5 10 6 static inline void nf_send_unreach(struct sk_buff *skb_in, int code) ··· 8 12 icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); 9 13 } 10 14 11 - /* Send RST reply */ 12 - static void nf_send_reset(struct sk_buff *oldskb, int hook) 13 - { 14 - struct sk_buff *nskb; 15 - const struct iphdr *oiph; 16 - struct iphdr *niph; 17 - const struct tcphdr *oth; 18 - struct tcphdr _otcph, *tcph; 19 - 20 - /* IP header checks: fragment. */ 21 - if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) 22 - return; 23 - 24 - oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), 25 - sizeof(_otcph), &_otcph); 26 - if (oth == NULL) 27 - return; 28 - 29 - /* No RST for RST. */ 30 - if (oth->rst) 31 - return; 32 - 33 - if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) 34 - return; 35 - 36 - /* Check checksum */ 37 - if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) 38 - return; 39 - oiph = ip_hdr(oldskb); 40 - 41 - nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + 42 - LL_MAX_HEADER, GFP_ATOMIC); 43 - if (!nskb) 44 - return; 45 - 46 - skb_reserve(nskb, LL_MAX_HEADER); 47 - 48 - skb_reset_network_header(nskb); 49 - niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); 50 - niph->version = 4; 51 - niph->ihl = sizeof(struct iphdr) / 4; 52 - niph->tos = 0; 53 - niph->id = 0; 54 - niph->frag_off = htons(IP_DF); 55 - niph->protocol = IPPROTO_TCP; 56 - niph->check = 0; 57 - niph->saddr = oiph->daddr; 58 - niph->daddr = oiph->saddr; 59 - 60 - skb_reset_transport_header(nskb); 61 - tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); 62 - memset(tcph, 0, sizeof(*tcph)); 63 - tcph->source = oth->dest; 64 - tcph->dest = oth->source; 65 - tcph->doff = sizeof(struct tcphdr) / 4; 66 - 67 - if (oth->ack) 68 - tcph->seq = oth->ack_seq; 69 - else { 70 - tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + 71 - oldskb->len - ip_hdrlen(oldskb) - 72 - (oth->doff << 2)); 73 - tcph->ack = 1; 74 - } 75 - 76 - tcph->rst = 1; 77 - tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr, 78 - niph->daddr, 0); 79 - nskb->ip_summed = CHECKSUM_PARTIAL; 80 - nskb->csum_start = (unsigned char *)tcph - nskb->head; 81 - nskb->csum_offset = offsetof(struct tcphdr, check); 82 - 83 - /* ip_route_me_harder expects skb->dst to be set */ 84 - skb_dst_set_noref(nskb, skb_dst(oldskb)); 85 - 86 - nskb->protocol = htons(ETH_P_IP); 87 - if (ip_route_me_harder(nskb, RTN_UNSPEC)) 88 - goto free_nskb; 89 - 90 - niph->ttl = ip4_dst_hoplimit(skb_dst(nskb)); 91 - 92 - /* "Never happens" */ 93 - if (nskb->len > dst_mtu(skb_dst(nskb))) 94 - goto free_nskb; 95 - 96 - nf_ct_attach(nskb, oldskb); 97 - 98 - #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 99 - /* If we use ip_local_out for bridged traffic, the MAC source on 100 - * the RST will be ours, instead of the destination's. This confuses 101 - * some routers/firewalls, and they drop the packet. So we need to 102 - * build the eth header using the original destination's MAC as the 103 - * source, and send the RST packet directly. 104 - */ 105 - if (oldskb->nf_bridge) { 106 - struct ethhdr *oeth = eth_hdr(oldskb); 107 - nskb->dev = oldskb->nf_bridge->physindev; 108 - niph->tot_len = htons(nskb->len); 109 - ip_send_check(niph); 110 - if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 111 - oeth->h_source, oeth->h_dest, nskb->len) < 0) 112 - goto free_nskb; 113 - dev_queue_xmit(nskb); 114 - } else 115 - #endif 116 - ip_local_out(nskb); 117 - 118 - return; 119 - 120 - free_nskb: 121 - kfree_skb(nskb); 122 - } 123 - 15 + void nf_send_reset(struct sk_buff *oldskb, int hook); 124 16 125 17 #endif /* _IPV4_NF_REJECT_H */
+6
net/ipv4/netfilter/Kconfig
··· 61 61 fields such as the source, destination, type of service and 62 62 the packet mark. 63 63 64 + config NF_REJECT_IPV4 65 + tristate "IPv4 packet rejection" 66 + default m if NETFILTER_ADVANCED=n 67 + 64 68 config NFT_REJECT_IPV4 65 69 depends on NF_TABLES_IPV4 70 + select NF_REJECT_IPV4 66 71 default NFT_REJECT 67 72 tristate 68 73 ··· 213 208 config IP_NF_TARGET_REJECT 214 209 tristate "REJECT target support" 215 210 depends on IP_NF_FILTER 211 + select NF_REJECT_IPV4 216 212 default m if NETFILTER_ADVANCED=n 217 213 help 218 214 The REJECT target allows a filtering rule to specify that an ICMP
+3
net/ipv4/netfilter/Makefile
··· 23 23 obj-$(CONFIG_NF_LOG_ARP) += nf_log_arp.o 24 24 obj-$(CONFIG_NF_LOG_IPV4) += nf_log_ipv4.o 25 25 26 + # reject 27 + obj-$(CONFIG_NF_REJECT_IPV4) += nf_reject_ipv4.o 28 + 26 29 # NAT helpers (nf_conntrack) 27 30 obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o 28 31 obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o
+127
net/ipv4/netfilter/nf_reject_ipv4.c
··· 1 + /* (C) 1999-2001 Paul `Rusty' Russell 2 + * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + */ 8 + 9 + #include <net/ip.h> 10 + #include <net/tcp.h> 11 + #include <net/route.h> 12 + #include <net/dst.h> 13 + #include <linux/netfilter_ipv4.h> 14 + 15 + /* Send RST reply */ 16 + void nf_send_reset(struct sk_buff *oldskb, int hook) 17 + { 18 + struct sk_buff *nskb; 19 + const struct iphdr *oiph; 20 + struct iphdr *niph; 21 + const struct tcphdr *oth; 22 + struct tcphdr _otcph, *tcph; 23 + 24 + /* IP header checks: fragment. */ 25 + if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) 26 + return; 27 + 28 + oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), 29 + sizeof(_otcph), &_otcph); 30 + if (oth == NULL) 31 + return; 32 + 33 + /* No RST for RST. */ 34 + if (oth->rst) 35 + return; 36 + 37 + if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) 38 + return; 39 + 40 + /* Check checksum */ 41 + if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) 42 + return; 43 + oiph = ip_hdr(oldskb); 44 + 45 + nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + 46 + LL_MAX_HEADER, GFP_ATOMIC); 47 + if (!nskb) 48 + return; 49 + 50 + skb_reserve(nskb, LL_MAX_HEADER); 51 + 52 + skb_reset_network_header(nskb); 53 + niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); 54 + niph->version = 4; 55 + niph->ihl = sizeof(struct iphdr) / 4; 56 + niph->tos = 0; 57 + niph->id = 0; 58 + niph->frag_off = htons(IP_DF); 59 + niph->protocol = IPPROTO_TCP; 60 + niph->check = 0; 61 + niph->saddr = oiph->daddr; 62 + niph->daddr = oiph->saddr; 63 + 64 + skb_reset_transport_header(nskb); 65 + tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); 66 + memset(tcph, 0, sizeof(*tcph)); 67 + tcph->source = oth->dest; 68 + tcph->dest = oth->source; 69 + tcph->doff = sizeof(struct tcphdr) / 4; 70 + 71 + if (oth->ack) 72 + tcph->seq = oth->ack_seq; 73 + else { 74 + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + 75 + oldskb->len - ip_hdrlen(oldskb) - 76 + (oth->doff << 2)); 77 + tcph->ack = 1; 78 + } 79 + 80 + tcph->rst = 1; 81 + tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr, 82 + niph->daddr, 0); 83 + nskb->ip_summed = CHECKSUM_PARTIAL; 84 + nskb->csum_start = (unsigned char *)tcph - nskb->head; 85 + nskb->csum_offset = offsetof(struct tcphdr, check); 86 + 87 + /* ip_route_me_harder expects skb->dst to be set */ 88 + skb_dst_set_noref(nskb, skb_dst(oldskb)); 89 + 90 + nskb->protocol = htons(ETH_P_IP); 91 + if (ip_route_me_harder(nskb, RTN_UNSPEC)) 92 + goto free_nskb; 93 + 94 + niph->ttl = ip4_dst_hoplimit(skb_dst(nskb)); 95 + 96 + /* "Never happens" */ 97 + if (nskb->len > dst_mtu(skb_dst(nskb))) 98 + goto free_nskb; 99 + 100 + nf_ct_attach(nskb, oldskb); 101 + 102 + #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 103 + /* If we use ip_local_out for bridged traffic, the MAC source on 104 + * the RST will be ours, instead of the destination's. This confuses 105 + * some routers/firewalls, and they drop the packet. So we need to 106 + * build the eth header using the original destination's MAC as the 107 + * source, and send the RST packet directly. 108 + */ 109 + if (oldskb->nf_bridge) { 110 + struct ethhdr *oeth = eth_hdr(oldskb); 111 + nskb->dev = oldskb->nf_bridge->physindev; 112 + niph->tot_len = htons(nskb->len); 113 + ip_send_check(niph); 114 + if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 115 + oeth->h_source, oeth->h_dest, nskb->len) < 0) 116 + goto free_nskb; 117 + dev_queue_xmit(nskb); 118 + } else 119 + #endif 120 + ip_local_out(nskb); 121 + 122 + return; 123 + 124 + free_nskb: 125 + kfree_skb(nskb); 126 + } 127 + EXPORT_SYMBOL_GPL(nf_send_reset);
+6
net/ipv6/netfilter/Kconfig
··· 40 40 fields such as the source, destination, flowlabel, hop-limit and 41 41 the packet mark. 42 42 43 + config NF_REJECT_IPV6 44 + tristate "IPv6 packet rejection" 45 + default m if NETFILTER_ADVANCED=n 46 + 43 47 config NFT_REJECT_IPV6 44 48 depends on NF_TABLES_IPV6 49 + select NF_REJECT_IPV6 45 50 default NFT_REJECT 46 51 tristate 47 52 ··· 213 208 config IP6_NF_TARGET_REJECT 214 209 tristate "REJECT target support" 215 210 depends on IP6_NF_FILTER 211 + select NF_REJECT_IPV6 216 212 default m if NETFILTER_ADVANCED=n 217 213 help 218 214 The REJECT target allows a filtering rule to specify that an ICMPv6
+3
net/ipv6/netfilter/Makefile
··· 27 27 # logging 28 28 obj-$(CONFIG_NF_LOG_IPV6) += nf_log_ipv6.o 29 29 30 + # reject 31 + obj-$(CONFIG_NF_REJECT_IPV6) += nf_reject_ipv6.o 32 + 30 33 # nf_tables 31 34 obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o 32 35 obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
+163
net/ipv6/netfilter/nf_reject_ipv6.c
··· 1 + /* (C) 1999-2001 Paul `Rusty' Russell 2 + * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + */ 8 + #include <net/ipv6.h> 9 + #include <net/ip6_route.h> 10 + #include <net/ip6_fib.h> 11 + #include <net/ip6_checksum.h> 12 + #include <linux/netfilter_ipv6.h> 13 + 14 + void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) 15 + { 16 + struct sk_buff *nskb; 17 + struct tcphdr otcph, *tcph; 18 + unsigned int otcplen, hh_len; 19 + int tcphoff, needs_ack; 20 + const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); 21 + struct ipv6hdr *ip6h; 22 + #define DEFAULT_TOS_VALUE 0x0U 23 + const __u8 tclass = DEFAULT_TOS_VALUE; 24 + struct dst_entry *dst = NULL; 25 + u8 proto; 26 + __be16 frag_off; 27 + struct flowi6 fl6; 28 + 29 + if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || 30 + (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { 31 + pr_debug("addr is not unicast.\n"); 32 + return; 33 + } 34 + 35 + proto = oip6h->nexthdr; 36 + tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off); 37 + 38 + if ((tcphoff < 0) || (tcphoff > oldskb->len)) { 39 + pr_debug("Cannot get TCP header.\n"); 40 + return; 41 + } 42 + 43 + otcplen = oldskb->len - tcphoff; 44 + 45 + /* IP header checks: fragment, too short. */ 46 + if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { 47 + pr_debug("proto(%d) != IPPROTO_TCP, " 48 + "or too short. otcplen = %d\n", 49 + proto, otcplen); 50 + return; 51 + } 52 + 53 + if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) 54 + BUG(); 55 + 56 + /* No RST for RST. */ 57 + if (otcph.rst) { 58 + pr_debug("RST is set\n"); 59 + return; 60 + } 61 + 62 + /* Check checksum. */ 63 + if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { 64 + pr_debug("TCP checksum is invalid\n"); 65 + return; 66 + } 67 + 68 + memset(&fl6, 0, sizeof(fl6)); 69 + fl6.flowi6_proto = IPPROTO_TCP; 70 + fl6.saddr = oip6h->daddr; 71 + fl6.daddr = oip6h->saddr; 72 + fl6.fl6_sport = otcph.dest; 73 + fl6.fl6_dport = otcph.source; 74 + security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); 75 + dst = ip6_route_output(net, NULL, &fl6); 76 + if (dst == NULL || dst->error) { 77 + dst_release(dst); 78 + return; 79 + } 80 + dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); 81 + if (IS_ERR(dst)) 82 + return; 83 + 84 + hh_len = (dst->dev->hard_header_len + 15)&~15; 85 + nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) 86 + + sizeof(struct tcphdr) + dst->trailer_len, 87 + GFP_ATOMIC); 88 + 89 + if (!nskb) { 90 + net_dbg_ratelimited("cannot alloc skb\n"); 91 + dst_release(dst); 92 + return; 93 + } 94 + 95 + skb_dst_set(nskb, dst); 96 + 97 + skb_reserve(nskb, hh_len + dst->header_len); 98 + 99 + skb_put(nskb, sizeof(struct ipv6hdr)); 100 + skb_reset_network_header(nskb); 101 + ip6h = ipv6_hdr(nskb); 102 + ip6_flow_hdr(ip6h, tclass, 0); 103 + ip6h->hop_limit = ip6_dst_hoplimit(dst); 104 + ip6h->nexthdr = IPPROTO_TCP; 105 + ip6h->saddr = oip6h->daddr; 106 + ip6h->daddr = oip6h->saddr; 107 + 108 + skb_reset_transport_header(nskb); 109 + tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); 110 + /* Truncate to length (no data) */ 111 + tcph->doff = sizeof(struct tcphdr)/4; 112 + tcph->source = otcph.dest; 113 + tcph->dest = otcph.source; 114 + 115 + if (otcph.ack) { 116 + needs_ack = 0; 117 + tcph->seq = otcph.ack_seq; 118 + tcph->ack_seq = 0; 119 + } else { 120 + needs_ack = 1; 121 + tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin 122 + + otcplen - (otcph.doff<<2)); 123 + tcph->seq = 0; 124 + } 125 + 126 + /* Reset flags */ 127 + ((u_int8_t *)tcph)[13] = 0; 128 + tcph->rst = 1; 129 + tcph->ack = needs_ack; 130 + tcph->window = 0; 131 + tcph->urg_ptr = 0; 132 + tcph->check = 0; 133 + 134 + /* Adjust TCP checksum */ 135 + tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, 136 + &ipv6_hdr(nskb)->daddr, 137 + sizeof(struct tcphdr), IPPROTO_TCP, 138 + csum_partial(tcph, 139 + sizeof(struct tcphdr), 0)); 140 + 141 + nf_ct_attach(nskb, oldskb); 142 + 143 + #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 144 + /* If we use ip6_local_out for bridged traffic, the MAC source on 145 + * the RST will be ours, instead of the destination's. This confuses 146 + * some routers/firewalls, and they drop the packet. So we need to 147 + * build the eth header using the original destination's MAC as the 148 + * source, and send the RST packet directly. 149 + */ 150 + if (oldskb->nf_bridge) { 151 + struct ethhdr *oeth = eth_hdr(oldskb); 152 + nskb->dev = oldskb->nf_bridge->physindev; 153 + nskb->protocol = htons(ETH_P_IPV6); 154 + ip6h->payload_len = htons(sizeof(struct tcphdr)); 155 + if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 156 + oeth->h_source, oeth->h_dest, nskb->len) < 0) 157 + return; 158 + dev_queue_xmit(nskb); 159 + } else 160 + #endif 161 + ip6_local_out(nskb); 162 + } 163 + EXPORT_SYMBOL_GPL(nf_send_reset6);