1cc70d069SEric Leblond #ifndef _IPV6_NF_REJECT_H 2cc70d069SEric Leblond #define _IPV6_NF_REJECT_H 3cc70d069SEric Leblond 4cc70d069SEric Leblond #include <net/ipv6.h> 5cc70d069SEric Leblond #include <net/ip6_route.h> 6cc70d069SEric Leblond #include <net/ip6_fib.h> 7cc70d069SEric Leblond #include <net/ip6_checksum.h> 8cc70d069SEric Leblond #include <linux/netfilter_ipv6.h> 9cc70d069SEric Leblond 10cc70d069SEric Leblond static inline void 11cc70d069SEric Leblond nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code, 12cc70d069SEric Leblond unsigned int hooknum) 13cc70d069SEric Leblond { 14cc70d069SEric Leblond if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) 15cc70d069SEric Leblond skb_in->dev = net->loopback_dev; 16cc70d069SEric Leblond 17cc70d069SEric Leblond icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); 18cc70d069SEric Leblond } 19cc70d069SEric Leblond 20cc70d069SEric Leblond /* Send RST reply */ 21cc70d069SEric Leblond static void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) 22cc70d069SEric Leblond { 23cc70d069SEric Leblond struct sk_buff *nskb; 24cc70d069SEric Leblond struct tcphdr otcph, *tcph; 25cc70d069SEric Leblond unsigned int otcplen, hh_len; 26cc70d069SEric Leblond int tcphoff, needs_ack; 27cc70d069SEric Leblond const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); 28cc70d069SEric Leblond struct ipv6hdr *ip6h; 29cc70d069SEric Leblond #define DEFAULT_TOS_VALUE 0x0U 30cc70d069SEric Leblond const __u8 tclass = DEFAULT_TOS_VALUE; 31cc70d069SEric Leblond struct dst_entry *dst = NULL; 32cc70d069SEric Leblond u8 proto; 33cc70d069SEric Leblond __be16 frag_off; 34cc70d069SEric Leblond struct flowi6 fl6; 35cc70d069SEric Leblond 36cc70d069SEric Leblond if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || 37cc70d069SEric Leblond (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { 38cc70d069SEric Leblond pr_debug("addr is not unicast.\n"); 39cc70d069SEric Leblond return; 40cc70d069SEric Leblond } 41cc70d069SEric Leblond 42cc70d069SEric Leblond proto = oip6h->nexthdr; 43cc70d069SEric Leblond tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off); 44cc70d069SEric Leblond 45cc70d069SEric Leblond if ((tcphoff < 0) || (tcphoff > oldskb->len)) { 46cc70d069SEric Leblond pr_debug("Cannot get TCP header.\n"); 47cc70d069SEric Leblond return; 48cc70d069SEric Leblond } 49cc70d069SEric Leblond 50cc70d069SEric Leblond otcplen = oldskb->len - tcphoff; 51cc70d069SEric Leblond 52cc70d069SEric Leblond /* IP header checks: fragment, too short. */ 53cc70d069SEric Leblond if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { 54cc70d069SEric Leblond pr_debug("proto(%d) != IPPROTO_TCP, " 55cc70d069SEric Leblond "or too short. otcplen = %d\n", 56cc70d069SEric Leblond proto, otcplen); 57cc70d069SEric Leblond return; 58cc70d069SEric Leblond } 59cc70d069SEric Leblond 60cc70d069SEric Leblond if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) 61cc70d069SEric Leblond BUG(); 62cc70d069SEric Leblond 63cc70d069SEric Leblond /* No RST for RST. */ 64cc70d069SEric Leblond if (otcph.rst) { 65cc70d069SEric Leblond pr_debug("RST is set\n"); 66cc70d069SEric Leblond return; 67cc70d069SEric Leblond } 68cc70d069SEric Leblond 69cc70d069SEric Leblond /* Check checksum. */ 70cc70d069SEric Leblond if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { 71cc70d069SEric Leblond pr_debug("TCP checksum is invalid\n"); 72cc70d069SEric Leblond return; 73cc70d069SEric Leblond } 74cc70d069SEric Leblond 75cc70d069SEric Leblond memset(&fl6, 0, sizeof(fl6)); 76cc70d069SEric Leblond fl6.flowi6_proto = IPPROTO_TCP; 77cc70d069SEric Leblond fl6.saddr = oip6h->daddr; 78cc70d069SEric Leblond fl6.daddr = oip6h->saddr; 79cc70d069SEric Leblond fl6.fl6_sport = otcph.dest; 80cc70d069SEric Leblond fl6.fl6_dport = otcph.source; 81cc70d069SEric Leblond security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); 82cc70d069SEric Leblond dst = ip6_route_output(net, NULL, &fl6); 83cc70d069SEric Leblond if (dst == NULL || dst->error) { 84cc70d069SEric Leblond dst_release(dst); 85cc70d069SEric Leblond return; 86cc70d069SEric Leblond } 87cc70d069SEric Leblond dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); 88cc70d069SEric Leblond if (IS_ERR(dst)) 89cc70d069SEric Leblond return; 90cc70d069SEric Leblond 91cc70d069SEric Leblond hh_len = (dst->dev->hard_header_len + 15)&~15; 92cc70d069SEric Leblond nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) 93cc70d069SEric Leblond + sizeof(struct tcphdr) + dst->trailer_len, 94cc70d069SEric Leblond GFP_ATOMIC); 95cc70d069SEric Leblond 96cc70d069SEric Leblond if (!nskb) { 97cc70d069SEric Leblond net_dbg_ratelimited("cannot alloc skb\n"); 98cc70d069SEric Leblond dst_release(dst); 99cc70d069SEric Leblond return; 100cc70d069SEric Leblond } 101cc70d069SEric Leblond 102cc70d069SEric Leblond skb_dst_set(nskb, dst); 103cc70d069SEric Leblond 104cc70d069SEric Leblond skb_reserve(nskb, hh_len + dst->header_len); 105cc70d069SEric Leblond 106cc70d069SEric Leblond skb_put(nskb, sizeof(struct ipv6hdr)); 107cc70d069SEric Leblond skb_reset_network_header(nskb); 108cc70d069SEric Leblond ip6h = ipv6_hdr(nskb); 109cc70d069SEric Leblond ip6_flow_hdr(ip6h, tclass, 0); 110cc70d069SEric Leblond ip6h->hop_limit = ip6_dst_hoplimit(dst); 111cc70d069SEric Leblond ip6h->nexthdr = IPPROTO_TCP; 112cc70d069SEric Leblond ip6h->saddr = oip6h->daddr; 113cc70d069SEric Leblond ip6h->daddr = oip6h->saddr; 114cc70d069SEric Leblond 115cc70d069SEric Leblond skb_reset_transport_header(nskb); 116cc70d069SEric Leblond tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); 117cc70d069SEric Leblond /* Truncate to length (no data) */ 118cc70d069SEric Leblond tcph->doff = sizeof(struct tcphdr)/4; 119cc70d069SEric Leblond tcph->source = otcph.dest; 120cc70d069SEric Leblond tcph->dest = otcph.source; 121cc70d069SEric Leblond 122cc70d069SEric Leblond if (otcph.ack) { 123cc70d069SEric Leblond needs_ack = 0; 124cc70d069SEric Leblond tcph->seq = otcph.ack_seq; 125cc70d069SEric Leblond tcph->ack_seq = 0; 126cc70d069SEric Leblond } else { 127cc70d069SEric Leblond needs_ack = 1; 128cc70d069SEric Leblond tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin 129cc70d069SEric Leblond + otcplen - (otcph.doff<<2)); 130cc70d069SEric Leblond tcph->seq = 0; 131cc70d069SEric Leblond } 132cc70d069SEric Leblond 133cc70d069SEric Leblond /* Reset flags */ 134cc70d069SEric Leblond ((u_int8_t *)tcph)[13] = 0; 135cc70d069SEric Leblond tcph->rst = 1; 136cc70d069SEric Leblond tcph->ack = needs_ack; 137cc70d069SEric Leblond tcph->window = 0; 138cc70d069SEric Leblond tcph->urg_ptr = 0; 139cc70d069SEric Leblond tcph->check = 0; 140cc70d069SEric Leblond 141cc70d069SEric Leblond /* Adjust TCP checksum */ 142cc70d069SEric Leblond tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, 143cc70d069SEric Leblond &ipv6_hdr(nskb)->daddr, 144cc70d069SEric Leblond sizeof(struct tcphdr), IPPROTO_TCP, 145cc70d069SEric Leblond csum_partial(tcph, 146cc70d069SEric Leblond sizeof(struct tcphdr), 0)); 147cc70d069SEric Leblond 148cc70d069SEric Leblond nf_ct_attach(nskb, oldskb); 149cc70d069SEric Leblond 150cc70d069SEric Leblond #ifdef CONFIG_BRIDGE_NETFILTER 151cc70d069SEric Leblond /* If we use ip6_local_out for bridged traffic, the MAC source on 152cc70d069SEric Leblond * the RST will be ours, instead of the destination's. This confuses 153cc70d069SEric Leblond * some routers/firewalls, and they drop the packet. So we need to 154cc70d069SEric Leblond * build the eth header using the original destination's MAC as the 155cc70d069SEric Leblond * source, and send the RST packet directly. 156cc70d069SEric Leblond */ 157cc70d069SEric Leblond if (oldskb->nf_bridge) { 158cc70d069SEric Leblond struct ethhdr *oeth = eth_hdr(oldskb); 159cc70d069SEric Leblond nskb->dev = oldskb->nf_bridge->physindev; 160cc70d069SEric Leblond nskb->protocol = htons(ETH_P_IPV6); 161cc70d069SEric Leblond ip6h->payload_len = htons(sizeof(struct tcphdr)); 162cc70d069SEric Leblond if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 163cc70d069SEric Leblond oeth->h_source, oeth->h_dest, nskb->len) < 0) 164cc70d069SEric Leblond return; 165cc70d069SEric Leblond dev_queue_xmit(nskb); 166cc70d069SEric Leblond } else 167cc70d069SEric Leblond #endif 168cc70d069SEric Leblond ip6_local_out(nskb); 169cc70d069SEric Leblond } 170cc70d069SEric Leblond 171cc70d069SEric Leblond #endif /* _IPV6_NF_REJECT_H */ 172