static int tcf_csum_ipv4_igmp(struct sk_buff *skb, unsigned int ihl, unsigned int ipl) { struct igmphdr *igmph; igmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*igmph)); if (igmph == NULL) return 0; igmph->csum = 0; skb->csum = csum_partial(igmph, ipl - ihl, 0); igmph->csum = csum_fold(skb->csum); skb->ip_summed = CHECKSUM_NONE; return 1; }
static int dccp_v4_verify_checksum(struct sk_buff *skb, const __be32 saddr, const __be32 daddr) { struct dccp_hdr *dh = dccp_hdr(skb); int checksum_len; u32 tmp; if (dh->dccph_cscov == 0) checksum_len = skb->len; else { checksum_len = (dh->dccph_cscov + dh->dccph_x) * sizeof(u32); checksum_len = checksum_len < skb->len ? checksum_len : skb->len; } tmp = csum_partial((unsigned char *)dh, checksum_len, 0); return csum_tcpudp_magic(saddr, daddr, checksum_len, IPPROTO_DCCP, tmp) == 0 ? 0 : -1; }
int skb_copy_and_csum_datagram_kvec(const struct sk_buff *skb, int offset, struct kvec *vec, int len) { unsigned int csum; struct kvec_dst dst; csum = csum_partial(skb->data, offset, skb->csum); kvec_dst_init(&dst, KM_USER0); kvec_dst_set(&dst, vec->veclet); kvec_dst_map(&dst); skb_copy_and_csum_datagram_kvec_dst(skb, offset, &dst, len, &csum); kvec_dst_unmap(&dst); if ((unsigned short)csum_fold(csum)) return -EINVAL; return 0; }
static int tcf_csum_ipv4_tcp(struct sk_buff *skb, struct iphdr *iph, unsigned int ihl, unsigned int ipl) { struct tcphdr *tcph; tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); if (tcph == NULL) return 0; tcph->check = 0; skb->csum = csum_partial(tcph, ipl - ihl, 0); tcph->check = tcp_v4_check(ipl - ihl, iph->saddr, iph->daddr, skb->csum); skb->ip_summed = CHECKSUM_NONE; return 1; }
void os_info_check(struct os_info *os_info) { if (os_info == NULL) kdump_failed(EOS_INFO_MISSING); if (((unsigned long) os_info) % PAGE_SIZE) kdump_failed(EOS_INFO_MISSING); if (!page_is_valid((unsigned long) os_info)) kdump_failed(EOS_INFO_MISSING); if (os_info->magic != OS_INFO_MAGIC) kdump_failed(EOS_INFO_MISSING); if (csum_partial(&os_info->version_major, OS_INF0_CSUM_SIZE, 0) != os_info->csum) kdump_failed(EOS_INFO_CSUM_FAILED); if (os_info->version_major > OS_INFO_VERSION_MAJOR_SUPPORTED) kdump_failed(EOS_INFO_VERSION); if (os_info->crashkernel_addr == 0) kdump_failed(EOS_INFO_NOCRASHKERNEL); }
/* Compute the whole skb csum in s/w and store it, then verify GRO csum * starting from gro_offset. */ static __sum16 gro_skb_checksum(struct sk_buff *skb) { __sum16 sum; skb->csum = skb_checksum(skb, 0, skb->len, 0); NAPI_GRO_CB(skb)->csum = csum_sub(skb->csum, csum_partial(skb->data, skb_gro_offset(skb), 0)); sum = csum_fold(NAPI_GRO_CB(skb)->csum); if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) { if (unlikely(!sum) && !skb->csum_complete_sw) netdev_rx_csum_fault(skb->dev); } else { skb->ip_summed = CHECKSUM_COMPLETE; skb->csum_complete_sw = 1; } return sum; }
static int tcf_csum_ipv6_icmp(struct sk_buff *skb, struct ipv6hdr *ip6h, unsigned int ihl, unsigned int ipl) { struct icmp6hdr *icmp6h; icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h)); if (icmp6h == NULL) return 0; icmp6h->icmp6_cksum = 0; skb->csum = csum_partial(icmp6h, ipl - ihl, 0); icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ipl - ihl, IPPROTO_ICMPV6, skb->csum); skb->ip_summed = CHECKSUM_NONE; return 1; }
static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan) { if (unlikely(vlan_tx_tag_present(skb))) { u16 current_tag; /* push down current VLAN tag */ current_tag = vlan_tx_tag_get(skb); if (!__vlan_put_tag(skb, current_tag)) return -ENOMEM; if (get_ip_summed(skb) == OVS_CSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(skb->data + ETH_HLEN, VLAN_HLEN, 0)); } __vlan_hwaccel_put_tag(skb, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); return 0; }
/* validate checksum of entire packet */ void csum_network_interface::test4(void) { /* we produced */ unsigned char pkt[]={ /* pseudo header */ 0xfe,0x80,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x16,0x3e,0xff,0xfe,0x11,0x34,0x24, 0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x3a, /* icmp payload */ 0x9b, 0x02, 0x39, 0x8d, 0x01, 0x40, 0x00, 0x01, 0x70, 0x61, 0x6e, 0x64, 0x6f, 0x72, 0x61, 0x20, 0x69, 0x73, 0x20, 0x66, 0x75, 0x6e, 0x0a, 0x6c }; unsigned short result = csum_partial(pkt, sizeof(pkt), 0); result = (~result & 0xffff); assert(result == 0); }
void tcp_send_check(struct sk_buff *skb) { if (skb_is_nonlinear(skb)) { skb_linearize(skb); } struct iphdr *ip_header = ip_hdr(skb); struct tcphdr *tcp_header = tcp_hdr(skb); unsigned int tcp_header_length = (skb->len - (ip_header->ihl << 2)); tcp_header->check = 0; tcp_header->check = tcp_v4_check( tcp_header_length, ip_header->saddr, ip_header->daddr, csum_partial( (char*)tcp_header, tcp_header_length, 0 ) ); skb->ip_summed = CHECKSUM_NONE; }
/* validate calculation of pseudo-header */ void csum_network_interface::test3(void) { struct ip6_hdr v6; unsigned char src[]={ 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x2, 0x16, 0x3e, 0xff, 0xfe, 0x11, 0x34, 0x24 }; unsigned char dst[]={ 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; memcpy(&v6.ip6_src.s6_addr, src, 16); memcpy(&v6.ip6_dst.s6_addr, dst, 16); unsigned int icmp_len = 24; // 0000: fe 80 00 00 00 00 00 00 02 16 3e ff fe 11 34 24 // 0010: ff 02 00 00 00 00 00 00 00 00 00 00 00 00 00 01 // 0020: 00 00 00 18 00 00 00 3a unsigned short icmp6sum = csum_ipv6_magic(&v6.ip6_src, &v6.ip6_dst, icmp_len, IPPROTO_ICMPV6, 0); icmp6sum = (~icmp6sum & 0xffff); unsigned char all[]={ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x16, 0x3e, 0xff, 0xfe, 0x11, 0x34, 0x24, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3a }; unsigned short result2 = csum_partial(all, 5*8, 0); result2 = (~result2 & 0xffff); printf("result2=%04x\n",result2); assert(result2 == htons(0x8edd)); assert(icmp6sum == result2); }
static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan) { if (unlikely(vlan_tx_tag_present(skb))) { u16 current_tag; /* push down current VLAN tag */ current_tag = vlan_tx_tag_get(skb); skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, current_tag); if (!skb) return -ENOMEM; if (skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(skb->data + (2 * ETH_ALEN), VLAN_HLEN, 0)); } __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); return 0; }
static int tcf_csum_ipv6_tcp(struct sk_buff *skb, unsigned int ihl, unsigned int ipl) { struct tcphdr *tcph; const struct ipv6hdr *ip6h; tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); if (tcph == NULL) return 0; ip6h = ipv6_hdr(skb); tcph->check = 0; skb->csum = csum_partial(tcph, ipl - ihl, 0); tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ipl - ihl, IPPROTO_TCP, skb->csum); skb->ip_summed = CHECKSUM_NONE; return 1; }
void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, const struct tcphdr *oth, unsigned int otcplen) { struct tcphdr *tcph; int needs_ack; skb_reset_transport_header(nskb); tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); /* Truncate to length (no data) */ tcph->doff = sizeof(struct tcphdr)/4; tcph->source = oth->dest; tcph->dest = oth->source; if (oth->ack) { needs_ack = 0; tcph->seq = oth->ack_seq; tcph->ack_seq = 0; } else { needs_ack = 1; tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + otcplen - (oth->doff<<2)); tcph->seq = 0; } /* Reset flags */ ((u_int8_t *)tcph)[13] = 0; tcph->rst = 1; tcph->ack = needs_ack; tcph->window = 0; tcph->urg_ptr = 0; tcph->check = 0; /* Adjust TCP checksum */ tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, &ipv6_hdr(nskb)->daddr, sizeof(struct tcphdr), IPPROTO_TCP, csum_partial(tcph, sizeof(struct tcphdr), 0)); }
__wsum csum_and_copy_from_user(const void __user *src, void *dst, int len, __wsum sum, int *err_ptr) { unsigned int csum; might_sleep(); *err_ptr = 0; if (!len) { csum = 0; goto out; } if (unlikely((len < 0) || !access_ok(VERIFY_READ, src, len))) { *err_ptr = -EFAULT; csum = (__force unsigned int)sum; goto out; } csum = csum_partial_copy_generic((void __force *)src, dst, len, sum, err_ptr, NULL); if (unlikely(*err_ptr)) { int missing = __copy_from_user(dst, src, len); if (missing) { memset(dst + len - missing, 0, missing); *err_ptr = -EFAULT; } else { *err_ptr = 0; } csum = csum_partial(dst, len, sum); } out: return (__force __wsum)csum; }
static bool ipv6_validate_packet_len(struct sk_buff *skb_in, struct sk_buff *skb_out) { struct ipv6hdr *ip6_hdr = ipv6_hdr(skb_out); struct hdr_iterator iterator = HDR_ITERATOR_INIT(ip6_hdr); unsigned int ipv6_mtu; unsigned int ipv4_mtu; if (skb_out->len <= skb_out->dev->mtu) return true; hdr_iterator_last(&iterator); if (iterator.hdr_type == IPPROTO_ICMPV6) { struct icmp6hdr *icmpv6_hdr = icmp6_hdr(skb_out); if (is_icmp6_error(icmpv6_hdr->icmp6_type)) { int new_packet_len = skb_out->dev->mtu; int l3_payload_len = new_packet_len - (iterator.data - (void *) ip6_hdr); skb_trim(skb_out, new_packet_len); ip6_hdr->payload_len = cpu_to_be16(l3_payload_len); icmpv6_hdr->icmp6_cksum = 0; icmpv6_hdr->icmp6_cksum = csum_ipv6_magic(&ip6_hdr->saddr, &ip6_hdr->daddr, l3_payload_len, IPPROTO_ICMPV6, csum_partial(icmpv6_hdr, l3_payload_len, 0)); return true; } } ipv6_mtu = skb_out->dev->mtu; ipv4_mtu = skb_in->dev->mtu; log_debug("Packet is too large for the outgoing MTU and IPv6 routers don't do fragmentation. " "Dropping..."); icmp_send(skb_in, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, cpu_to_be32(min_uint(ipv6_mtu, ipv4_mtu + 20))); return false; }
/** * skb_copy_and_csum_datagram_iovec - Copy and checkum skb to user iovec. * @skb: skbuff * @hlen: hardware length * @iov: io vector * * Caller _must_ check that skb will fit to this iovec. * * Returns: 0 - success. * -EINVAL - checksum failure. * -EFAULT - fault during copy. Beware, in this case iovec * can be modified! */ int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, int hlen, struct iovec *iov) { __wsum csum; int chunk = skb->len - hlen; if (!chunk) return 0; /* Skip filled elements. * Pretty silly, look at memcpy_toiovec, though 8) */ while (!iov->iov_len) iov++; if (iov->iov_len < chunk) { if (__skb_checksum_complete(skb)) goto csum_error; if (skb_copy_datagram_iovec(skb, hlen, iov, chunk)) goto fault; } else { csum = csum_partial(skb->data, hlen, skb->csum); if (skb_copy_and_csum_datagram(skb, hlen, iov->iov_base, chunk, &csum)) goto fault; if (csum_fold(csum)) goto csum_error; if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) netdev_rx_csum_fault(skb->dev); iov->iov_len -= chunk; iov->iov_base += chunk; } return 0; csum_error: return -EINVAL; fault: return -EFAULT; }
/* set ECT codepoint from IP header. * return 0 if there was an error. */ static inline int set_ect_ip(struct sk_buff **pskb, const struct ipt_ECN_info *einfo) { if (((*pskb)->nh.iph->tos & IPT_ECN_IP_MASK) != (einfo->ip_ect & IPT_ECN_IP_MASK)) { u_int16_t diffs[2]; if (!skb_ip_make_writable(pskb, sizeof(struct iphdr))) return 0; diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF; (*pskb)->nh.iph->tos &= ~IPT_ECN_IP_MASK; (*pskb)->nh.iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK); diffs[1] = htons((*pskb)->nh.iph->tos); (*pskb)->nh.iph->check = csum_fold(csum_partial((char *)diffs, sizeof(diffs), (*pskb)->nh.iph->check ^0xFFFF)); (*pskb)->nfcache |= NFC_ALTERED; } return 1; }
/* * Copy from userspace and compute checksum. If we catch an exception * then zero the rest of the buffer. */ unsigned int csum_partial_copy_from_user (const char *src, char *dst, int len, unsigned int sum, int *err_ptr) { int missing; #ifdef CONFIG_PHOENIX_FAST_CSUM unsigned int csum; #endif might_sleep(); missing = copy_from_user(dst, src, len); if (missing) { memset(dst + len - missing, 0, missing); *err_ptr = -EFAULT; } #ifdef CONFIG_PHOENIX_FAST_CSUM local_irq_disable(); csum = xlr_csum_partial_nocopy(dst, len, sum); local_irq_enable(); return csum; #else return csum_partial(dst, len, sum); #endif }
static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) { #if defined(CONFIG_FILTER) if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) { if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) { UDP6_INC_STATS_BH(UdpInErrors); IP6_INC_STATS_BH(Ip6InDiscards); kfree_skb(skb); return 0; } skb->ip_summed = CHECKSUM_UNNECESSARY; } #endif if (sock_queue_rcv_skb(sk,skb)<0) { UDP6_INC_STATS_BH(UdpInErrors); IP6_INC_STATS_BH(Ip6InDiscards); kfree_skb(skb); return 0; } IP6_INC_STATS_BH(Ip6InDelivers); UDP6_INC_STATS_BH(UdpInDatagrams); return 0; }
static int tcf_csum_ipv4_tcp(struct sk_buff *skb, unsigned int ihl, unsigned int ipl) { struct tcphdr *tcph; const struct iphdr *iph; if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) return 1; tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); if (tcph == NULL) return 0; iph = ip_hdr(skb); tcph->check = 0; skb->csum = csum_partial(tcph, ipl - ihl, 0); tcph->check = tcp_v4_check(ipl - ihl, iph->saddr, iph->daddr, skb->csum); skb->ip_summed = CHECKSUM_NONE; return 1; }
void qca_gmac_rx_qos(athr_gmac_t *mac, struct sk_buff *skb) { uint16_t hdr_type = 0; int tos; struct ethhdr *eh; unsigned short diffs[2]; /* extract the header */ hdr_type = *(uint16_t *)(skb->data + 12); if ((hdr_type & HDR_PACKET_TYPE_MASK) == ATHR_NORMAL_PKT) { /* get the priority from the header */ tos = AC_TO_ENET_TOS (((hdr_type >> HDR_PRIORITY_SHIFT) & HDR_PRIORITY_MASK)); #ifndef CONFIG_ATHR_VLAN_IGMP athr_remove_hdr(mac,skb); #endif eh = (struct ethhdr *)skb->data; if (eh->h_proto == __constant_htons(ETHERTYPE_IP)) { struct iphdr *ip = (struct iphdr *) (skb->data + sizeof (struct ethhdr)); /* * IP frame: exclude ECN bits 0-1 and map DSCP bits 2-7 * from TOS byte. */ tos = (tos << TOS_ECN_SHIFT ) & TOS_ECN_MASK; if ( tos != ip->tos ) { diffs[0] = htons(ip->tos) ^ 0xFFFF; ip->tos = ((ip->tos & ~(TOS_ECN_MASK)) | tos); diffs[1] = htons(ip->tos); ip->check = csum_fold(csum_partial((char *)diffs, sizeof(diffs),ip->check ^ 0xFFFF)); } skb->priority = tos; } }
static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan) { /* * Push an eventual existing hardware accel VLAN tag to the skb first * to maintain correct order. */ if (unlikely(vlan_tx_tag_present(skb))) { u16 current_tag; /* push down current VLAN tag */ current_tag = vlan_tx_tag_get(skb); if (!__vlan_put_tag(skb, current_tag)) return -ENOMEM; if (skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(skb->data + ETH_HLEN, VLAN_HLEN, 0)); } __vlan_put_tag(skb, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); return 0; }
static int internal_dev_recv(struct vport *vport, struct sk_buff *skb) { struct net_device *netdev = netdev_vport_priv(vport)->dev; int len; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) if (vlan_tx_tag_present(skb)) { if (unlikely(!__vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb)))) return 0; if (skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(skb->data + (2 * ETH_ALEN), VLAN_HLEN, 0)); vlan_set_tci(skb, 0); } #endif len = skb->len; skb_dst_drop(skb); nf_reset(skb); secpath_reset(skb); skb->dev = netdev; skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, netdev); skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); netif_rx(skb); return len; }
struct sk_buff *ndisc_build_skb(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *saddr, struct icmp6hdr *icmp6h, const struct in6_addr *target, int llinfo) { struct net *net = dev_net(dev); struct sock *sk = net->ipv6.ndisc_sk; struct sk_buff *skb; struct icmp6hdr *hdr; int hlen = LL_RESERVED_SPACE(dev); int tlen = dev->needed_tailroom; int len; u8 *opt; if (!dev->addr_len) llinfo = 0; len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0); if (llinfo) len += ndisc_opt_addr_space(dev); skb = alloc_skb((MAX_HEADER + sizeof(struct ipv6hdr) + len + hlen + tlen), GFP_ATOMIC); if (!skb) { ND_PRINTK0(KERN_ERR "ICMPv6 ND: %s() failed to allocate an skb.\n", __func__); return NULL; } skb_reserve(skb, hlen); ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); skb->transport_header = skb->tail; skb_put(skb, len); hdr = (struct icmp6hdr *)skb_transport_header(skb); memcpy(hdr, icmp6h, sizeof(*hdr)); opt = skb_transport_header(skb) + sizeof(struct icmp6hdr); if (target) { ipv6_addr_copy((struct in6_addr *)opt, target); opt += sizeof(*target); } if (llinfo) ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev->addr_len, dev->type); hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, csum_partial(hdr, len, 0)); /* Manually assign socket ownership as we avoid calling * sock_alloc_send_pskb() to bypass wmem buffer limits */ skb_set_owner_w(skb, sk); return skb; }
int nat25_db_handle(_adapter *priv, struct sk_buff *skb, int method) { unsigned short protocol; unsigned char networkAddr[MAX_NETWORK_ADDR_LEN]; unsigned int tmp; if (skb == NULL) return -1; if ((method <= NAT25_MIN) || (method >= NAT25_MAX)) return -1; protocol = be16_to_cpu(*((__be16 *)(skb->data + 2 * ETH_ALEN))); /*---------------------------------------------------*/ /* Handle IP frame */ /*---------------------------------------------------*/ if (protocol == ETH_P_IP) { struct iphdr* iph = (struct iphdr *)(skb->data + ETH_HLEN); if (((unsigned char*)(iph) + (iph->ihl<<2)) >= (skb->data + ETH_HLEN + skb->len)) { DEBUG_WARN("NAT25: malformed IP packet !\n"); return -1; } switch (method) { case NAT25_CHECK: return -1; case NAT25_INSERT: { /* some muticast with source IP is all zero, maybe other case is illegal */ /* in class A, B, C, host address is all zero or all one is illegal */ if (iph->saddr == 0) return 0; tmp = be32_to_cpu(iph->saddr); DEBUG_INFO("NAT25: Insert IP, SA=%08x, DA=%08x\n", tmp, iph->daddr); __nat25_generate_ipv4_network_addr(networkAddr, &tmp); /* record source IP address and , source mac address into db */ __nat25_db_network_insert(priv, skb->data+ETH_ALEN, networkAddr); __nat25_db_print(priv); } return 0; case NAT25_LOOKUP: { DEBUG_INFO("NAT25: Lookup IP, SA=%08x, DA=%08x\n", iph->saddr, iph->daddr); tmp = be32_to_cpu(iph->daddr); __nat25_generate_ipv4_network_addr(networkAddr, &tmp); if (!__nat25_db_network_lookup_and_replace(priv, skb, networkAddr)) { if (*((unsigned char *)&iph->daddr + 3) == 0xff) { /* L2 is unicast but L3 is broadcast, make L2 bacome broadcast */ DEBUG_INFO("NAT25: Set DA as boardcast\n"); memset(skb->data, 0xff, ETH_ALEN); } else { /* forward unknow IP packet to upper TCP/IP */ DEBUG_INFO("NAT25: Replace DA with BR's MAC\n"); if ( (*(u32 *)priv->br_mac) == 0 && (*(u16 *)(priv->br_mac+4)) == 0 ) { printk("Re-init netdev_br_init() due to br_mac==0!\n"); netdev_br_init(priv->pnetdev); } memcpy(skb->data, priv->br_mac, ETH_ALEN); } } } return 0; default: return -1; } } /*---------------------------------------------------*/ /* Handle ARP frame */ /*---------------------------------------------------*/ else if (protocol == ETH_P_ARP) { struct arphdr *arp = (struct arphdr *)(skb->data + ETH_HLEN); unsigned char *arp_ptr = (unsigned char *)(arp + 1); unsigned int *sender, *target; if (arp->ar_pro != __constant_htons(ETH_P_IP)) { DEBUG_WARN("NAT25: arp protocol unknown (%4x)!\n", be16_to_cpu(arp->ar_pro)); return -1; } switch (method) { case NAT25_CHECK: return 0; /* skb_copy for all ARP frame */ case NAT25_INSERT: { DEBUG_INFO("NAT25: Insert ARP, MAC=%02x%02x%02x%02x%02x%02x\n", arp_ptr[0], arp_ptr[1], arp_ptr[2], arp_ptr[3], arp_ptr[4], arp_ptr[5]); /* change to ARP sender mac address to wlan STA address */ memcpy(arp_ptr, GET_MY_HWADDR(priv), ETH_ALEN); arp_ptr += arp->ar_hln; sender = (unsigned int *)arp_ptr; __nat25_generate_ipv4_network_addr(networkAddr, sender); __nat25_db_network_insert(priv, skb->data+ETH_ALEN, networkAddr); __nat25_db_print(priv); } return 0; case NAT25_LOOKUP: { DEBUG_INFO("NAT25: Lookup ARP\n"); arp_ptr += arp->ar_hln; sender = (unsigned int *)arp_ptr; arp_ptr += (arp->ar_hln + arp->ar_pln); target = (unsigned int *)arp_ptr; __nat25_generate_ipv4_network_addr(networkAddr, target); __nat25_db_network_lookup_and_replace(priv, skb, networkAddr); /* change to ARP target mac address to Lookup result */ arp_ptr = (unsigned char *)(arp + 1); arp_ptr += (arp->ar_hln + arp->ar_pln); memcpy(arp_ptr, skb->data, ETH_ALEN); } return 0; default: return -1; } } /*---------------------------------------------------*/ /* Handle IPX and Apple Talk frame */ /*---------------------------------------------------*/ else if ((protocol == ETH_P_IPX) || (protocol <= ETH_FRAME_LEN)) { unsigned char ipx_header[2] = {0xFF, 0xFF}; struct ipxhdr *ipx = NULL; struct elapaarp *ea = NULL; struct ddpehdr *ddp = NULL; unsigned char *framePtr = skb->data + ETH_HLEN; if (protocol == ETH_P_IPX) { DEBUG_INFO("NAT25: Protocol=IPX (Ethernet II)\n"); ipx = (struct ipxhdr *)framePtr; } else if (protocol <= ETH_FRAME_LEN) { if (!memcmp(ipx_header, framePtr, 2)) { DEBUG_INFO("NAT25: Protocol=IPX (Ethernet 802.3)\n"); ipx = (struct ipxhdr *)framePtr; } else { unsigned char ipx_8022_type = 0xE0; unsigned char snap_8022_type = 0xAA; if (*framePtr == snap_8022_type) { unsigned char ipx_snap_id[5] = {0x0, 0x0, 0x0, 0x81, 0x37}; /* IPX SNAP ID */ unsigned char aarp_snap_id[5] = {0x00, 0x00, 0x00, 0x80, 0xF3}; /* Apple Talk AARP SNAP ID */ unsigned char ddp_snap_id[5] = {0x08, 0x00, 0x07, 0x80, 0x9B}; /* Apple Talk DDP SNAP ID */ framePtr += 3; /* eliminate the 802.2 header */ if (!memcmp(ipx_snap_id, framePtr, 5)) { framePtr += 5; /* eliminate the SNAP header */ DEBUG_INFO("NAT25: Protocol=IPX (Ethernet SNAP)\n"); ipx = (struct ipxhdr *)framePtr; } else if (!memcmp(aarp_snap_id, framePtr, 5)) { framePtr += 5; /* eliminate the SNAP header */ ea = (struct elapaarp *)framePtr; } else if (!memcmp(ddp_snap_id, framePtr, 5)) { framePtr += 5; /* eliminate the SNAP header */ ddp = (struct ddpehdr *)framePtr; } else { DEBUG_WARN("NAT25: Protocol=Ethernet SNAP %02x%02x%02x%02x%02x\n", framePtr[0], framePtr[1], framePtr[2], framePtr[3], framePtr[4]); return -1; } } else if (*framePtr == ipx_8022_type) { framePtr += 3; /* eliminate the 802.2 header */ if (!memcmp(ipx_header, framePtr, 2)) { DEBUG_INFO("NAT25: Protocol=IPX (Ethernet 802.2)\n"); ipx = (struct ipxhdr *)framePtr; } else return -1; } else return -1; } } else return -1; /* IPX */ if (ipx != NULL) { switch (method) { case NAT25_CHECK: if (!memcmp(skb->data+ETH_ALEN, ipx->ipx_source.node, ETH_ALEN)) { DEBUG_INFO("NAT25: Check IPX skb_copy\n"); return 0; } return -1; case NAT25_INSERT: { DEBUG_INFO("NAT25: Insert IPX, Dest=%08x,%02x%02x%02x%02x%02x%02x,%04x Source=%08x,%02x%02x%02x%02x%02x%02x,%04x\n", ipx->ipx_dest.net, ipx->ipx_dest.node[0], ipx->ipx_dest.node[1], ipx->ipx_dest.node[2], ipx->ipx_dest.node[3], ipx->ipx_dest.node[4], ipx->ipx_dest.node[5], ipx->ipx_dest.sock, ipx->ipx_source.net, ipx->ipx_source.node[0], ipx->ipx_source.node[1], ipx->ipx_source.node[2], ipx->ipx_source.node[3], ipx->ipx_source.node[4], ipx->ipx_source.node[5], ipx->ipx_source.sock); if (!memcmp(skb->data+ETH_ALEN, ipx->ipx_source.node, ETH_ALEN)) { DEBUG_INFO("NAT25: Use IPX Net, and Socket as network addr\n"); __nat25_generate_ipx_network_addr_with_socket(networkAddr, &ipx->ipx_source.net, &ipx->ipx_source.sock); /* change IPX source node addr to wlan STA address */ memcpy(ipx->ipx_source.node, GET_MY_HWADDR(priv), ETH_ALEN); } else { __nat25_generate_ipx_network_addr_with_node(networkAddr, &ipx->ipx_source.net, ipx->ipx_source.node); } __nat25_db_network_insert(priv, skb->data+ETH_ALEN, networkAddr); __nat25_db_print(priv); } return 0; case NAT25_LOOKUP: { if (!memcmp(GET_MY_HWADDR(priv), ipx->ipx_dest.node, ETH_ALEN)) { DEBUG_INFO("NAT25: Lookup IPX, Modify Destination IPX Node addr\n"); __nat25_generate_ipx_network_addr_with_socket(networkAddr, &ipx->ipx_dest.net, &ipx->ipx_dest.sock); __nat25_db_network_lookup_and_replace(priv, skb, networkAddr); /* replace IPX destination node addr with Lookup destination MAC addr */ memcpy(ipx->ipx_dest.node, skb->data, ETH_ALEN); } else { __nat25_generate_ipx_network_addr_with_node(networkAddr, &ipx->ipx_dest.net, ipx->ipx_dest.node); __nat25_db_network_lookup_and_replace(priv, skb, networkAddr); } } return 0; default: return -1; } } /* AARP */ else if (ea != NULL) { /* Sanity check fields. */ if (ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN) { DEBUG_WARN("NAT25: Appletalk AARP Sanity check fail!\n"); return -1; } switch (method) { case NAT25_CHECK: return 0; case NAT25_INSERT: { /* change to AARP source mac address to wlan STA address */ memcpy(ea->hw_src, GET_MY_HWADDR(priv), ETH_ALEN); DEBUG_INFO("NAT25: Insert AARP, Source=%d,%d Destination=%d,%d\n", ea->pa_src_net, ea->pa_src_node, ea->pa_dst_net, ea->pa_dst_node); __nat25_generate_apple_network_addr(networkAddr, &ea->pa_src_net, &ea->pa_src_node); __nat25_db_network_insert(priv, skb->data+ETH_ALEN, networkAddr); __nat25_db_print(priv); } return 0; case NAT25_LOOKUP: { DEBUG_INFO("NAT25: Lookup AARP, Source=%d,%d Destination=%d,%d\n", ea->pa_src_net, ea->pa_src_node, ea->pa_dst_net, ea->pa_dst_node); __nat25_generate_apple_network_addr(networkAddr, &ea->pa_dst_net, &ea->pa_dst_node); __nat25_db_network_lookup_and_replace(priv, skb, networkAddr); /* change to AARP destination mac address to Lookup result */ memcpy(ea->hw_dst, skb->data, ETH_ALEN); } return 0; default: return -1; } } /* DDP */ else if (ddp != NULL) { switch (method) { case NAT25_CHECK: return -1; case NAT25_INSERT: { DEBUG_INFO("NAT25: Insert DDP, Source=%d,%d Destination=%d,%d\n", ddp->deh_snet, ddp->deh_snode, ddp->deh_dnet, ddp->deh_dnode); __nat25_generate_apple_network_addr(networkAddr, &ddp->deh_snet, &ddp->deh_snode); __nat25_db_network_insert(priv, skb->data+ETH_ALEN, networkAddr); __nat25_db_print(priv); } return 0; case NAT25_LOOKUP: { DEBUG_INFO("NAT25: Lookup DDP, Source=%d,%d Destination=%d,%d\n", ddp->deh_snet, ddp->deh_snode, ddp->deh_dnet, ddp->deh_dnode); __nat25_generate_apple_network_addr(networkAddr, &ddp->deh_dnet, &ddp->deh_dnode); __nat25_db_network_lookup_and_replace(priv, skb, networkAddr); } return 0; default: return -1; } } return -1; } /*---------------------------------------------------*/ /* Handle PPPoE frame */ /*---------------------------------------------------*/ else if ((protocol == ETH_P_PPP_DISC) || (protocol == ETH_P_PPP_SES)) { struct pppoe_hdr *ph = (struct pppoe_hdr *)(skb->data + ETH_HLEN); unsigned short *pMagic; switch (method) { case NAT25_CHECK: if (ph->sid == 0) return 0; return 1; case NAT25_INSERT: if (ph->sid == 0) /* Discovery phase according to tag */ { if (ph->code == PADI_CODE || ph->code == PADR_CODE) { if (priv->ethBrExtInfo.addPPPoETag) { struct pppoe_tag *tag, *pOldTag; unsigned char tag_buf[40]; int old_tag_len=0; tag = (struct pppoe_tag *)tag_buf; pOldTag = (struct pppoe_tag *)__nat25_find_pppoe_tag(ph, ntohs(PTT_RELAY_SID)); if (pOldTag) { /* if SID existed, copy old value and delete it */ old_tag_len = ntohs(pOldTag->tag_len); if (old_tag_len+TAG_HDR_LEN+MAGIC_CODE_LEN+RTL_RELAY_TAG_LEN > sizeof(tag_buf)) { DEBUG_ERR("SID tag length too long!\n"); return -1; } memcpy(tag->tag_data+MAGIC_CODE_LEN+RTL_RELAY_TAG_LEN, pOldTag->tag_data, old_tag_len); if (skb_pull_and_merge(skb, (unsigned char *)pOldTag, TAG_HDR_LEN+old_tag_len) < 0) { DEBUG_ERR("call skb_pull_and_merge() failed in PADI/R packet!\n"); return -1; } ph->length = htons(ntohs(ph->length)-TAG_HDR_LEN-old_tag_len); } tag->tag_type = PTT_RELAY_SID; tag->tag_len = htons(MAGIC_CODE_LEN+RTL_RELAY_TAG_LEN+old_tag_len); /* insert the magic_code+client mac in relay tag */ pMagic = (unsigned short *)tag->tag_data; *pMagic = htons(MAGIC_CODE); memcpy(tag->tag_data+MAGIC_CODE_LEN, skb->data+ETH_ALEN, ETH_ALEN); /* Add relay tag */ if (__nat25_add_pppoe_tag(skb, tag) < 0) return -1; DEBUG_INFO("NAT25: Insert PPPoE, forward %s packet\n", (ph->code == PADI_CODE ? "PADI" : "PADR")); } else { /* not add relay tag */ if (priv->pppoe_connection_in_progress && memcmp(skb->data+ETH_ALEN, priv->pppoe_addr, ETH_ALEN)) { DEBUG_ERR("Discard PPPoE packet due to another PPPoE connection is in progress!\n"); return -2; } if (priv->pppoe_connection_in_progress == 0) memcpy(priv->pppoe_addr, skb->data+ETH_ALEN, ETH_ALEN); priv->pppoe_connection_in_progress = WAIT_TIME_PPPOE; } } else return -1; } else /* session phase */ { DEBUG_INFO("NAT25: Insert PPPoE, insert session packet to %s\n", skb->dev->name); __nat25_generate_pppoe_network_addr(networkAddr, skb->data, &(ph->sid)); __nat25_db_network_insert(priv, skb->data+ETH_ALEN, networkAddr); __nat25_db_print(priv); if (!priv->ethBrExtInfo.addPPPoETag && priv->pppoe_connection_in_progress && !memcmp(skb->data+ETH_ALEN, priv->pppoe_addr, ETH_ALEN)) priv->pppoe_connection_in_progress = 0; } return 0; case NAT25_LOOKUP: if (ph->code == PADO_CODE || ph->code == PADS_CODE) { if (priv->ethBrExtInfo.addPPPoETag) { struct pppoe_tag *tag; unsigned char *ptr; unsigned short tagType, tagLen; int offset=0; if ((ptr = __nat25_find_pppoe_tag(ph, ntohs(PTT_RELAY_SID))) == NULL) { DEBUG_ERR("Fail to find PTT_RELAY_SID in FADO!\n"); return -1; } tag = (struct pppoe_tag *)ptr; tagType = (unsigned short)((ptr[0] << 8) + ptr[1]); tagLen = (unsigned short)((ptr[2] << 8) + ptr[3]); if ((tagType != ntohs(PTT_RELAY_SID)) || (tagLen < (MAGIC_CODE_LEN+RTL_RELAY_TAG_LEN))) { DEBUG_ERR("Invalid PTT_RELAY_SID tag length [%d]!\n", tagLen); return -1; } pMagic = (unsigned short *)tag->tag_data; if (ntohs(*pMagic) != MAGIC_CODE) { DEBUG_ERR("Can't find MAGIC_CODE in %s packet!\n", (ph->code == PADO_CODE ? "PADO" : "PADS")); return -1; } memcpy(skb->data, tag->tag_data+MAGIC_CODE_LEN, ETH_ALEN); if (tagLen > MAGIC_CODE_LEN+RTL_RELAY_TAG_LEN) offset = TAG_HDR_LEN; if (skb_pull_and_merge(skb, ptr+offset, TAG_HDR_LEN+MAGIC_CODE_LEN+RTL_RELAY_TAG_LEN-offset) < 0) { DEBUG_ERR("call skb_pull_and_merge() failed in PADO packet!\n"); return -1; } ph->length = htons(ntohs(ph->length)-(TAG_HDR_LEN+MAGIC_CODE_LEN+RTL_RELAY_TAG_LEN-offset)); if (offset > 0) tag->tag_len = htons(tagLen-MAGIC_CODE_LEN-RTL_RELAY_TAG_LEN); DEBUG_INFO("NAT25: Lookup PPPoE, forward %s Packet from %s\n", (ph->code == PADO_CODE ? "PADO" : "PADS"), skb->dev->name); } else { /* not add relay tag */ if (!priv->pppoe_connection_in_progress) { DEBUG_ERR("Discard PPPoE packet due to no connection in progresss!\n"); return -1; } memcpy(skb->data, priv->pppoe_addr, ETH_ALEN); priv->pppoe_connection_in_progress = WAIT_TIME_PPPOE; } } else { if (ph->sid != 0) { DEBUG_INFO("NAT25: Lookup PPPoE, lookup session packet from %s\n", skb->dev->name); __nat25_generate_pppoe_network_addr(networkAddr, skb->data+ETH_ALEN, &(ph->sid)); __nat25_db_network_lookup_and_replace(priv, skb, networkAddr); __nat25_db_print(priv); } else return -1; } return 0; default: return -1; } } /*---------------------------------------------------*/ /* Handle EAP frame */ /*---------------------------------------------------*/ else if (protocol == 0x888e) { switch (method) { case NAT25_CHECK: return -1; case NAT25_INSERT: return 0; case NAT25_LOOKUP: return 0; default: return -1; } } /*---------------------------------------------------*/ /* Handle C-Media proprietary frame */ /*---------------------------------------------------*/ else if ((protocol == 0xe2ae) || (protocol == 0xe2af)) { switch (method) { case NAT25_CHECK: return -1; case NAT25_INSERT: return 0; case NAT25_LOOKUP: return 0; default: return -1; } } /*---------------------------------------------------*/ /* Handle IPV6 frame */ /*---------------------------------------------------*/ else if (protocol == ETH_P_IPV6) { struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + ETH_HLEN); if (sizeof(*iph) >= (skb->len - ETH_HLEN)) { DEBUG_WARN("NAT25: malformed IPv6 packet !\n"); return -1; } switch (method) { case NAT25_CHECK: if (skb->data[0] & 1) return 0; return -1; case NAT25_INSERT: { DEBUG_INFO("NAT25: Insert IP, SA=%4x:%4x:%4x:%4x:%4x:%4x:%4x:%4x," " DA=%4x:%4x:%4x:%4x:%4x:%4x:%4x:%4x\n", iph->saddr.s6_addr16[0],iph->saddr.s6_addr16[1],iph->saddr.s6_addr16[2],iph->saddr.s6_addr16[3], iph->saddr.s6_addr16[4],iph->saddr.s6_addr16[5],iph->saddr.s6_addr16[6],iph->saddr.s6_addr16[7], iph->daddr.s6_addr16[0],iph->daddr.s6_addr16[1],iph->daddr.s6_addr16[2],iph->daddr.s6_addr16[3], iph->daddr.s6_addr16[4],iph->daddr.s6_addr16[5],iph->daddr.s6_addr16[6],iph->daddr.s6_addr16[7]); if (memcmp(&iph->saddr, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16)) { __nat25_generate_ipv6_network_addr(networkAddr, (unsigned int *)&iph->saddr); __nat25_db_network_insert(priv, skb->data+ETH_ALEN, networkAddr); __nat25_db_print(priv); if (iph->nexthdr == IPPROTO_ICMPV6 && skb->len > (ETH_HLEN + sizeof(*iph) + 4)) { if (update_nd_link_layer_addr(skb->data + ETH_HLEN + sizeof(*iph), skb->len - ETH_HLEN - sizeof(*iph), GET_MY_HWADDR(priv))) { struct icmp6hdr *hdr = (struct icmp6hdr *)(skb->data + ETH_HLEN + sizeof(*iph)); hdr->icmp6_cksum = 0; hdr->icmp6_cksum = csum_ipv6_magic(&iph->saddr, &iph->daddr, iph->payload_len, IPPROTO_ICMPV6, csum_partial((__u8 *)hdr, iph->payload_len, 0)); } } } } return 0; case NAT25_LOOKUP: DEBUG_INFO("NAT25: Lookup IP, SA=%4x:%4x:%4x:%4x:%4x:%4x:%4x:%4x," " DA=%4x:%4x:%4x:%4x:%4x:%4x:%4x:%4x\n", iph->saddr.s6_addr16[0],iph->saddr.s6_addr16[1],iph->saddr.s6_addr16[2],iph->saddr.s6_addr16[3], iph->saddr.s6_addr16[4],iph->saddr.s6_addr16[5],iph->saddr.s6_addr16[6],iph->saddr.s6_addr16[7], iph->daddr.s6_addr16[0],iph->daddr.s6_addr16[1],iph->daddr.s6_addr16[2],iph->daddr.s6_addr16[3], iph->daddr.s6_addr16[4],iph->daddr.s6_addr16[5],iph->daddr.s6_addr16[6],iph->daddr.s6_addr16[7]); __nat25_generate_ipv6_network_addr(networkAddr, (unsigned int *)&iph->daddr); __nat25_db_network_lookup_and_replace(priv, skb, networkAddr); return 0; default: return -1; } } return -1; }
/* Send RST reply */ static void send_reset(struct net *net, struct sk_buff *oldskb) { struct sk_buff *nskb; struct tcphdr otcph, *tcph; unsigned int otcplen, hh_len; int tcphoff, needs_ack; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); struct ipv6hdr *ip6h; #define DEFAULT_TOS_VALUE 0x0U const __u8 tclass = DEFAULT_TOS_VALUE; struct dst_entry *dst = NULL; u8 proto; struct flowi fl; if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { pr_debug("addr is not unicast.\n"); return; } proto = oip6h->nexthdr; tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto); if ((tcphoff < 0) || (tcphoff > oldskb->len)) { pr_debug("Cannot get TCP header.\n"); return; } otcplen = oldskb->len - tcphoff; /* IP header checks: fragment, too short. */ if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { pr_debug("proto(%d) != IPPROTO_TCP, " "or too short. otcplen = %d\n", proto, otcplen); return; } if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) BUG(); /* No RST for RST. */ if (otcph.rst) { pr_debug("RST is set\n"); return; } /* Check checksum. */ if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, skb_checksum(oldskb, tcphoff, otcplen, 0))) { pr_debug("TCP checksum is invalid\n"); return; } memset(&fl, 0, sizeof(fl)); fl.proto = IPPROTO_TCP; ipv6_addr_copy(&fl.fl6_src, &oip6h->daddr); ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr); fl.fl_ip_sport = otcph.dest; fl.fl_ip_dport = otcph.source; security_skb_classify_flow(oldskb, &fl); dst = ip6_route_output(net, NULL, &fl); if (dst == NULL || dst->error) { dst_release(dst); return; } if (xfrm_lookup(net, &dst, &fl, NULL, 0)) return; hh_len = (dst->dev->hard_header_len + 15)&~15; nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + dst->trailer_len, GFP_ATOMIC); if (!nskb) { if (net_ratelimit()) pr_debug("cannot alloc skb\n"); dst_release(dst); return; } skb_dst_set(nskb, dst); skb_reserve(nskb, hh_len + dst->header_len); skb_put(nskb, sizeof(struct ipv6hdr)); skb_reset_network_header(nskb); ip6h = ipv6_hdr(nskb); *(__be32 *)ip6h = htonl(0x60000000 | (tclass << 20)); ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); ip6h->nexthdr = IPPROTO_TCP; ipv6_addr_copy(&ip6h->saddr, &oip6h->daddr); ipv6_addr_copy(&ip6h->daddr, &oip6h->saddr); tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); /* Truncate to length (no data) */ tcph->doff = sizeof(struct tcphdr)/4; tcph->source = otcph.dest; tcph->dest = otcph.source; if (otcph.ack) { needs_ack = 0; tcph->seq = otcph.ack_seq; tcph->ack_seq = 0; } else { needs_ack = 1; tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin + otcplen - (otcph.doff<<2)); tcph->seq = 0; } /* Reset flags */ ((u_int8_t *)tcph)[13] = 0; tcph->rst = 1; tcph->ack = needs_ack; tcph->window = 0; tcph->urg_ptr = 0; tcph->check = 0; /* Adjust TCP checksum */ tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, &ipv6_hdr(nskb)->daddr, sizeof(struct tcphdr), IPPROTO_TCP, csum_partial(tcph, sizeof(struct tcphdr), 0)); nf_ct_attach(nskb, oldskb); ip6_local_out(nskb); }
/* Send RST reply */ static void send_reset(struct sk_buff *oldskb, int hook) { struct sk_buff *nskb; const struct iphdr *oiph; struct iphdr *niph; const struct tcphdr *oth; struct tcphdr _otcph, *tcph; unsigned int addr_type; /* IP header checks: fragment. */ if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) return; oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), sizeof(_otcph), &_otcph); if (oth == NULL) return; /* No RST for RST. */ if (oth->rst) return; /* Check checksum */ if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) return; oiph = ip_hdr(oldskb); nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + LL_MAX_HEADER, GFP_ATOMIC); if (!nskb) return; skb_reserve(nskb, LL_MAX_HEADER); skb_reset_network_header(nskb); niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); niph->version = 4; niph->ihl = sizeof(struct iphdr) / 4; niph->tos = 0; niph->id = 0; niph->frag_off = htons(IP_DF); niph->protocol = IPPROTO_TCP; niph->check = 0; niph->saddr = oiph->daddr; niph->daddr = oiph->saddr; tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); memset(tcph, 0, sizeof(*tcph)); tcph->source = oth->dest; tcph->dest = oth->source; tcph->doff = sizeof(struct tcphdr) / 4; if (oth->ack) tcph->seq = oth->ack_seq; else { tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + oldskb->len - ip_hdrlen(oldskb) - (oth->doff << 2)); tcph->ack = 1; } tcph->rst = 1; tcph->check = tcp_v4_check(sizeof(struct tcphdr), niph->saddr, niph->daddr, csum_partial(tcph, sizeof(struct tcphdr), 0)); addr_type = RTN_UNSPEC; if (hook != NF_INET_FORWARD #ifdef CONFIG_BRIDGE_NETFILTER || (nskb->nf_bridge && nskb->nf_bridge->mask & BRNF_BRIDGED) #endif ) addr_type = RTN_LOCAL; /* ip_route_me_harder expects skb->dst to be set */ skb_dst_set(nskb, dst_clone(skb_dst(oldskb))); if (ip_route_me_harder(nskb, addr_type)) goto free_nskb; niph->ttl = dst_metric(skb_dst(nskb), RTAX_HOPLIMIT); nskb->ip_summed = CHECKSUM_NONE; /* "Never happens" */ if (nskb->len > dst_mtu(skb_dst(nskb))) goto free_nskb; nf_ct_attach(nskb, oldskb); ip_local_out(nskb); return; free_nskb: kfree_skb(nskb); }
static void nft_reject_br_send_v4_unreach(struct net *net, struct sk_buff *oldskb, const struct net_device *dev, int hook, u8 code) { struct sk_buff *nskb; struct iphdr *niph; struct icmphdr *icmph; unsigned int len; void *payload; __wsum csum; u8 proto; if (oldskb->csum_bad || !nft_bridge_iphdr_validate(oldskb)) return; /* IP header checks: fragment. */ if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) return; /* RFC says return as much as we can without exceeding 576 bytes. */ len = min_t(unsigned int, 536, oldskb->len); if (!pskb_may_pull(oldskb, len)) return; if (pskb_trim_rcsum(oldskb, ntohs(ip_hdr(oldskb)->tot_len))) return; if (ip_hdr(oldskb)->protocol == IPPROTO_TCP || ip_hdr(oldskb)->protocol == IPPROTO_UDP) proto = ip_hdr(oldskb)->protocol; else proto = 0; if (!skb_csum_unnecessary(oldskb) && nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), proto)) return; nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) + LL_MAX_HEADER + len, GFP_ATOMIC); if (!nskb) return; skb_reserve(nskb, LL_MAX_HEADER); niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP, net->ipv4.sysctl_ip_default_ttl); skb_reset_transport_header(nskb); icmph = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr)); memset(icmph, 0, sizeof(*icmph)); icmph->type = ICMP_DEST_UNREACH; icmph->code = code; payload = skb_put(nskb, len); memcpy(payload, skb_network_header(oldskb), len); csum = csum_partial((void *)icmph, len + sizeof(struct icmphdr), 0); icmph->checksum = csum_fold(csum); niph->tot_len = htons(nskb->len); ip_send_check(niph); nft_reject_br_push_etherhdr(oldskb, nskb); br_forward(br_port_get_rcu(dev), nskb, false, true); }