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 <linux/module.h> 10 #include <net/ipv6.h> 11 #include <net/ip6_route.h> 12 #include <net/ip6_fib.h> 13 #include <net/ip6_checksum.h> 14 #include <net/netfilter/ipv6/nf_reject.h> 15 #include <linux/netfilter_ipv6.h> 16 #include <linux/netfilter_bridge.h> 17 #include <net/netfilter/ipv6/nf_reject.h> 18 19 const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb, 20 struct tcphdr *otcph, 21 unsigned int *otcplen, int hook) 22 { 23 const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); 24 u8 proto; 25 __be16 frag_off; 26 int tcphoff; 27 28 proto = oip6h->nexthdr; 29 tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), 30 &proto, &frag_off); 31 32 if ((tcphoff < 0) || (tcphoff > oldskb->len)) { 33 pr_debug("Cannot get TCP header.\n"); 34 return NULL; 35 } 36 37 *otcplen = oldskb->len - tcphoff; 38 39 /* IP header checks: fragment, too short. */ 40 if (proto != IPPROTO_TCP || *otcplen < sizeof(struct tcphdr)) { 41 pr_debug("proto(%d) != IPPROTO_TCP or too short (len = %d)\n", 42 proto, *otcplen); 43 return NULL; 44 } 45 46 otcph = skb_header_pointer(oldskb, tcphoff, sizeof(struct tcphdr), 47 otcph); 48 if (otcph == NULL) 49 return NULL; 50 51 /* No RST for RST. */ 52 if (otcph->rst) { 53 pr_debug("RST is set\n"); 54 return NULL; 55 } 56 57 /* Check checksum. */ 58 if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { 59 pr_debug("TCP checksum is invalid\n"); 60 return NULL; 61 } 62 63 return otcph; 64 } 65 EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_get); 66 67 struct ipv6hdr *nf_reject_ip6hdr_put(struct sk_buff *nskb, 68 const struct sk_buff *oldskb, 69 __u8 protocol, int hoplimit) 70 { 71 struct ipv6hdr *ip6h; 72 const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); 73 #define DEFAULT_TOS_VALUE 0x0U 74 const __u8 tclass = DEFAULT_TOS_VALUE; 75 76 skb_put(nskb, sizeof(struct ipv6hdr)); 77 skb_reset_network_header(nskb); 78 ip6h = ipv6_hdr(nskb); 79 ip6_flow_hdr(ip6h, tclass, 0); 80 ip6h->hop_limit = hoplimit; 81 ip6h->nexthdr = protocol; 82 ip6h->saddr = oip6h->daddr; 83 ip6h->daddr = oip6h->saddr; 84 85 nskb->protocol = htons(ETH_P_IPV6); 86 87 return ip6h; 88 } 89 EXPORT_SYMBOL_GPL(nf_reject_ip6hdr_put); 90 91 void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb, 92 const struct sk_buff *oldskb, 93 const struct tcphdr *oth, unsigned int otcplen) 94 { 95 struct tcphdr *tcph; 96 int needs_ack; 97 98 skb_reset_transport_header(nskb); 99 tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); 100 /* Truncate to length (no data) */ 101 tcph->doff = sizeof(struct tcphdr)/4; 102 tcph->source = oth->dest; 103 tcph->dest = oth->source; 104 105 if (oth->ack) { 106 needs_ack = 0; 107 tcph->seq = oth->ack_seq; 108 tcph->ack_seq = 0; 109 } else { 110 needs_ack = 1; 111 tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + 112 otcplen - (oth->doff<<2)); 113 tcph->seq = 0; 114 } 115 116 /* Reset flags */ 117 ((u_int8_t *)tcph)[13] = 0; 118 tcph->rst = 1; 119 tcph->ack = needs_ack; 120 tcph->window = 0; 121 tcph->urg_ptr = 0; 122 tcph->check = 0; 123 124 /* Adjust TCP checksum */ 125 tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, 126 &ipv6_hdr(nskb)->daddr, 127 sizeof(struct tcphdr), IPPROTO_TCP, 128 csum_partial(tcph, 129 sizeof(struct tcphdr), 0)); 130 } 131 EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_put); 132 133 void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) 134 { 135 struct sk_buff *nskb; 136 struct tcphdr _otcph; 137 const struct tcphdr *otcph; 138 unsigned int otcplen, hh_len; 139 const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); 140 struct ipv6hdr *ip6h; 141 struct dst_entry *dst = NULL; 142 struct flowi6 fl6; 143 144 if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || 145 (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { 146 pr_debug("addr is not unicast.\n"); 147 return; 148 } 149 150 otcph = nf_reject_ip6_tcphdr_get(oldskb, &_otcph, &otcplen, hook); 151 if (!otcph) 152 return; 153 154 memset(&fl6, 0, sizeof(fl6)); 155 fl6.flowi6_proto = IPPROTO_TCP; 156 fl6.saddr = oip6h->daddr; 157 fl6.daddr = oip6h->saddr; 158 fl6.fl6_sport = otcph->dest; 159 fl6.fl6_dport = otcph->source; 160 security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); 161 dst = ip6_route_output(net, NULL, &fl6); 162 if (dst == NULL || dst->error) { 163 dst_release(dst); 164 return; 165 } 166 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); 167 if (IS_ERR(dst)) 168 return; 169 170 hh_len = (dst->dev->hard_header_len + 15)&~15; 171 nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) 172 + sizeof(struct tcphdr) + dst->trailer_len, 173 GFP_ATOMIC); 174 175 if (!nskb) { 176 net_dbg_ratelimited("cannot alloc skb\n"); 177 dst_release(dst); 178 return; 179 } 180 181 skb_dst_set(nskb, dst); 182 183 skb_reserve(nskb, hh_len + dst->header_len); 184 ip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, 185 ip6_dst_hoplimit(dst)); 186 nf_reject_ip6_tcphdr_put(nskb, oldskb, otcph, otcplen); 187 188 nf_ct_attach(nskb, oldskb); 189 190 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 191 /* If we use ip6_local_out for bridged traffic, the MAC source on 192 * the RST will be ours, instead of the destination's. This confuses 193 * some routers/firewalls, and they drop the packet. So we need to 194 * build the eth header using the original destination's MAC as the 195 * source, and send the RST packet directly. 196 */ 197 if (oldskb->nf_bridge) { 198 struct ethhdr *oeth = eth_hdr(oldskb); 199 200 nskb->dev = nf_bridge_get_physindev(oldskb); 201 nskb->protocol = htons(ETH_P_IPV6); 202 ip6h->payload_len = htons(sizeof(struct tcphdr)); 203 if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 204 oeth->h_source, oeth->h_dest, nskb->len) < 0) 205 return; 206 dev_queue_xmit(nskb); 207 } else 208 #endif 209 ip6_local_out(nskb); 210 } 211 EXPORT_SYMBOL_GPL(nf_send_reset6); 212 213 static bool reject6_csum_ok(struct sk_buff *skb, int hook) 214 { 215 const struct ipv6hdr *ip6h = ipv6_hdr(skb); 216 int thoff; 217 __be16 fo; 218 u8 proto; 219 220 if (skb->csum_bad) 221 return false; 222 223 if (skb_csum_unnecessary(skb)) 224 return true; 225 226 proto = ip6h->nexthdr; 227 thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo); 228 229 if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0) 230 return false; 231 232 return nf_ip6_checksum(skb, hook, thoff, proto) == 0; 233 } 234 235 void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, 236 unsigned char code, unsigned int hooknum) 237 { 238 if (!reject6_csum_ok(skb_in, hooknum)) 239 return; 240 241 if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) 242 skb_in->dev = net->loopback_dev; 243 244 icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); 245 } 246 EXPORT_SYMBOL_GPL(nf_send_unreach6); 247 248 MODULE_LICENSE("GPL"); 249