/* add pseudo-header to DCCP checksum stored in skb->csum */ static inline __sum16 dccp_v6_csum_finish(struct sk_buff *skb, const struct in6_addr *saddr, const struct in6_addr *daddr) { return csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_DCCP, skb->csum); }
int nat25_db_handle(_adapter *priv, struct sk_buff *skb, int method) { unsigned short protocol; unsigned char networkAddr[MAX_NETWORK_ADDR_LEN]; if(skb == NULL) return -1; if((method <= NAT25_MIN) || (method >= NAT25_MAX)) return -1; protocol = *((unsigned short *)(skb->data + 2 * ETH_ALEN)); /*---------------------------------------------------*/ /* Handle IP frame */ /*---------------------------------------------------*/ if(protocol == __constant_htons(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; DEBUG_INFO("NAT25: Insert IP, SA=%08x, DA=%08x\n", iph->saddr, iph->daddr); __nat25_generate_ipv4_network_addr(networkAddr, &iph->saddr); //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); #ifdef SUPPORT_TX_MCAST2UNI if (priv->pshare->rf_ft_var.mc2u_disable || ((((OPMODE & (WIFI_STATION_STATE|WIFI_ASOC_STATE)) == (WIFI_STATION_STATE|WIFI_ASOC_STATE)) && !checkIPMcAndReplace(priv, skb, &iph->daddr)) || (OPMODE & WIFI_ADHOC_STATE))) #endif { __nat25_generate_ipv4_network_addr(networkAddr, &iph->daddr); 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"); set_broadcast_mac_addr(skb->data); } else { // forward unknow IP packet to upper TCP/IP DEBUG_INFO("NAT25: Replace DA with BR's MAC\n"); if ( is_zero_mac_addr(priv->br_mac) ) { void netdev_br_init(struct net_device *netdev); printk("Re-init netdev_br_init() due to br_mac==0!\n"); netdev_br_init(priv->pnetdev); } copy_mac_addr(skb->data, priv->br_mac); } } } } return 0; default: return -1; } } /*---------------------------------------------------*/ /* Handle ARP frame */ /*---------------------------------------------------*/ else if(protocol == __constant_htons(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", htons(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 copy_mac_addr(arp_ptr, GET_MY_HWADDR(priv)); 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); copy_mac_addr(arp_ptr, skb->data); } return 0; default: return -1; } } /*---------------------------------------------------*/ /* Handle IPX and Apple Talk frame */ /*---------------------------------------------------*/ else if((protocol == __constant_htons(ETH_P_IPX)) || (protocol <= __constant_htons(ETH_FRAME_LEN))) { struct ipxhdr *ipx = NULL; struct elapaarp *ea = NULL; struct ddpehdr *ddp = NULL; unsigned char *framePtr = skb->data + ETH_HLEN; if(protocol == __constant_htons(ETH_P_IPX)) { DEBUG_INFO("NAT25: Protocol=IPX (Ethernet II)\n"); ipx = (struct ipxhdr *)framePtr; } else if(protocol <= __constant_htons(ETH_FRAME_LEN)) { if(RTW_RN16(framePtr) == 0xffff) { 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) { static const u8 ipx_snap_id[5] = {0x00, 0x00, 0x00, 0x81, 0x37}; // IPX SNAP ID static const u8 aarp_snap_id[5] = {0x00, 0x00, 0x00, 0x80, 0xF3}; // Apple Talk AARP SNAP ID static const u8 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 (RTW_RN16(framePtr) == 0xffff) { 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(mac_addr_equal(skb->data+ETH_ALEN, ipx->ipx_source.node)) { 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(mac_addr_equal(skb->data+ETH_ALEN, ipx->ipx_source.node)) { 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 copy_mac_addr(ipx->ipx_source.node, GET_MY_HWADDR(priv)); } 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(mac_addr_equal(GET_MY_HWADDR(priv), ipx->ipx_dest.node)) { 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 copy_mac_addr(ipx->ipx_dest.node, skb->data); } 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 copy_mac_addr(ea->hw_src, GET_MY_HWADDR(priv)); 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 copy_mac_addr(ea->hw_dst, skb->data); } 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 == __constant_htons(ETH_P_PPP_DISC)) || (protocol == __constant_htons(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); copy_mac_addr(tag->tag_data+MAGIC_CODE_LEN, skb->data+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 && !mac_addr_equal(skb->data+ETH_ALEN, priv->pppoe_addr)) { DEBUG_ERR("Discard PPPoE packet due to another PPPoE connection is in progress!\n"); return -2; } if (priv->pppoe_connection_in_progress == 0) copy_mac_addr(priv->pppoe_addr, skb->data+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 && mac_addr_equal(skb->data+ETH_ALEN, priv->pppoe_addr)) 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))) == 0) { DEBUG_ERR("Fail to find PTT_RELAY_SID in FADO!\n"); return -1; } tag = (struct pppoe_tag *)ptr; tagType = RTW_RB16(&ptr[0]); tagLen = RTW_RB16(&ptr[2]); 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; } copy_mac_addr(skb->data, tag->tag_data+MAGIC_CODE_LEN); 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; } copy_mac_addr(skb->data, priv->pppoe_addr); 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 == __constant_htons(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 == __constant_htons(0xe2ae)) || (protocol == __constant_htons(0xe2af))) { switch(method) { case NAT25_CHECK: return -1; case NAT25_INSERT: return 0; case NAT25_LOOKUP: return 0; default: return -1; } } /*---------------------------------------------------*/ /* Handle IPV6 frame */ /*---------------------------------------------------*/ #ifdef CL_IPV6_PASS else if(protocol == __constant_htons(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 (memchr_inv(&iph->saddr, 0, 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); if (!__nat25_db_network_lookup_and_replace(priv, skb, networkAddr)) { #ifdef SUPPORT_RX_UNI2MCAST if (iph->daddr.s6_addr[0] == 0xff) convert_ipv6_mac_to_mc(skb); #endif } return 0; default: return -1; } } #endif // CL_IPV6_PASS return -1; }
static void __ndisc_send(struct net_device *dev, struct neighbour *neigh, struct in6_addr *daddr, struct in6_addr *saddr, struct icmp6hdr *icmp6h, struct in6_addr *target, int llinfo, int icmp6_mib_outnd) { struct flowi fl; struct dst_entry *dst; struct sock *sk = ndisc_socket->sk; struct sk_buff *skb; struct icmp6hdr *hdr; struct inet6_dev *idev; int len; int err; u8 *opt; ndisc_flow_init(&fl, icmp6h->icmp6_type, saddr, daddr, dev->ifindex); dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); if (!dst) return; err = xfrm_lookup(&dst, &fl, NULL, 0); if (err < 0) return; if (!dev->addr_len) llinfo = 0; len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0); if (llinfo) len += ndisc_opt_addr_space(dev); skb = sock_alloc_send_skb(sk, (MAX_HEADER + sizeof(struct ipv6hdr) + len + LL_RESERVED_SPACE(dev)), 1, &err); if (!skb) { ND_PRINTK0(KERN_ERR "ICMPv6 ND: %s() failed to allocate an skb.\n", __FUNCTION__); dst_release(dst); return; } skb_reserve(skb, LL_RESERVED_SPACE(dev)); 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((__u8 *) hdr, len, 0)); skb->dst = dst; idev = in6_dev_get(dst->dev); IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); if (!err) { ICMP6_INC_STATS(idev, icmp6_mib_outnd); ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); } if (likely(idev != NULL)) in6_dev_put(idev); }
/* Send RST reply */ static void send_reset(struct vrf *vrf, u32 litevrf_id, struct sk_buff *oldskb) { struct sk_buff *nskb; struct tcphdr otcph, *tcph; unsigned int otcplen, hh_len; int tcphoff, needs_ack; struct ipv6hdr *oip6h = oldskb->nh.ipv6h, *ip6h; 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))) { DEBUGP("ip6t_REJECT: 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)) { DEBUGP("ip6t_REJECT: Can't get TCP header.\n"); return; } otcplen = oldskb->len - tcphoff; /* IP header checks: fragment, too short. */ if ((proto != IPPROTO_TCP) || (otcplen < sizeof(struct tcphdr))) { DEBUGP("ip6t_REJECT: 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) { DEBUGP("ip6t_REJECT: RST is set\n"); return; } /* Check checksum. */ if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, skb_checksum(oldskb, tcphoff, otcplen, 0))) { DEBUGP("ip6t_REJECT: 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(vrf, litevrf_id, NULL, &fl); if (dst == NULL) return; if (dst->error || xfrm_lookup(&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()) printk("ip6t_REJECT: Can't alloc skb\n"); dst_release(dst); return; } nskb->dst = dst; skb_reserve(nskb, hh_len + dst->header_len); ip6h = nskb->nh.ipv6h = (struct ipv6hdr *) skb_put(nskb, sizeof(struct ipv6hdr)); ip6h->version = 6; ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); ip6h->nexthdr = IPPROTO_TCP; ip6h->payload_len = htons(sizeof(struct tcphdr)); 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(&nskb->nh.ipv6h->saddr, &nskb->nh.ipv6h->daddr, sizeof(struct tcphdr), IPPROTO_TCP, csum_partial((char *)tcph, sizeof(struct tcphdr), 0)); nf_ct_attach(nskb, oldskb); NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev, dst_output); }
void pcap_network_interface::send_raw_icmp(struct in6_addr *dest, struct in6_addr *src, const unsigned char *icmp_body, const unsigned int icmp_len) { uint8_t all_hosts_addr[] = {0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; unsigned char packet[icmp_len+sizeof(ip6_hdr)+16]; if (dest == NULL) { dest = (struct in6_addr *)all_hosts_addr; /* XXX WRONG WRONG WRONG */ } /* layer 2 */ memset(packet, 0, sizeof(packet)); packet[0] =0x02; packet[1]=0x34; packet[2]=0x56; packet[3] =0x78; packet[4]=0x9a; packet[5]=0xbc; memcpy(packet+6, this->if_hwaddr, 6); packet[12]=0x86; packet[13]=0xdd; /* layer 3 */ unsigned char *layer3 = &packet[14]; struct ip6_hdr *v6 = (struct ip6_hdr *)layer3; v6->ip6_src = link_local(); if(src) { memcpy(&v6->ip6_src, src, 16); } v6->ip6_vfc = 6 << 4; memcpy(&v6->ip6_dst, dest, 16); v6->ip6_nxt = IPPROTO_ICMPV6; v6->ip6_hlim= 64; v6->ip6_plen= htons(icmp_len); /* layer 4+ */ unsigned char *payload = (unsigned char *)(v6+1); memcpy(payload, icmp_body, icmp_len); struct icmp6_hdr *icmp6h = (struct icmp6_hdr *)payload; /* compute checksum */ icmp6h->icmp6_cksum = 0; unsigned short icmp6sum = csum_ipv6_magic(&v6->ip6_src, &v6->ip6_dst, icmp_len, IPPROTO_ICMPV6, 0); #if 0 unsigned int chksum_len = icmp_len+sizeof(struct ip6_hdr); printf("iface output of %u bytes (%u)\n", chksum_len, icmp_len); hexdump(layer3, 0, chksum_len); #endif icmp6sum = csum_partial(payload, icmp_len, icmp6sum); icmp6sum = (~icmp6sum & 0xffff); icmp6h->icmp6_cksum = icmp6sum; struct pcap_pkthdr h; memset(&h, 0, sizeof(h)); h.caplen = 14+40+icmp_len; h.len = h.caplen; gettimeofday(&h.ts, NULL); if(this->pcap_out) { pcap_dump((u_char *)this->pcap_out, &h, packet); } }
bool ns_nat64_tcp_manip4_pkt(struct sk_buff *skb, unsigned int iphdroff, struct netsession_tuple *tuple) { s32 ret = NS_ACCEPT; struct iphdr ip4hdr = {0}; struct sk_buff * pskb = NULL; struct sk_buff * sk_tmp = NULL; struct tcphdr * tcp_hdr; struct ipv6hdr *ip6hdr = NULL; unsigned char iphdr_buff[NS_NAT64_IPHDROFF] = {0}; s32 realloc_len = (sizeof(struct ipv6hdr) - sizeof(struct iphdr))*2 + sizeof(struct frag_hdr) + sizeof(struct icmp6hdr) - sizeof(struct icmphdr); if((pskb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { ret = NS_DROP; goto out; } skb_linearize(pskb); if(skb_copy_bits(pskb, iphdroff, &ip4hdr, sizeof(struct iphdr)) < 0) { ret = NS_DROP; goto out; } if(ip4hdr.ttl < 2) { ret = NS_DROP; goto out; } if (skb_headroom(pskb) < realloc_len) { sk_tmp = pskb; pskb = skb_realloc_headroom(sk_tmp, realloc_len); kfree_skb(sk_tmp); if (!pskb) { ret = NS_DROP; goto out; } } if (iphdroff) { if (skb_copy_bits(pskb, 0, iphdr_buff, (iphdroff<=NS_NAT64_IPHDROFF) ? iphdroff : NS_NAT64_IPHDROFF)) { ret = NS_DROP; goto out; } } skb_pull(pskb, ((ip4hdr.ihl)<<2) + iphdroff); ip4hdr.tot_len -= (ip4hdr.ihl)<<2; tcp_hdr = (struct tcphdr *)(pskb->data + iphdroff); /* 端口转换 */ tcp_hdr->source = tuple->src.u.tcp.port; tcp_hdr->dest = tuple->dst.u.tcp.port; /* 更新校验和 */ tcp_hdr->check = 0; tcp_hdr->check = csum_ipv6_magic(&tuple->src.u3.in6, &tuple->dst.u3.in6, ip4hdr.tot_len, IPPROTO_TCP, csum_partial(tcp_hdr, ip4hdr.tot_len, 0)); skb_push(pskb, sizeof(struct ipv6hdr)); ip6hdr = (struct ipv6hdr*)(pskb->data + iphdroff); nat64_iphdr_4to6(&ip4hdr, ip6hdr, tuple); if (iphdroff) memcpy(skb->data, iphdr_buff, (iphdroff<=NS_NAT64_IPHDROFF) ? iphdroff : NS_NAT64_IPHDROFF); pskb->nh.raw = pskb->data; out: return ret; }
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; }
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; int err; 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 = sock_alloc_send_skb(sk, (MAX_HEADER + sizeof(struct ipv6hdr) + len + hlen + tlen), 1, &err); if (!skb) { ND_PRINTK0(KERN_ERR "ICMPv6 ND: %s() failed to allocate an skb, err=%d.\n", __func__, err); 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) { *(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)); return skb; }
static int tcp_fnat_out_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp) { struct tcphdr *tcph; unsigned int tcphoff; int oldlen; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6) tcphoff = sizeof(struct ipv6hdr); else #endif tcphoff = ip_hdrlen(skb); oldlen = skb->len - tcphoff; /* csum_check requires unshared skb */ if (!skb_make_writable(skb, tcphoff + sizeof(*tcph))) return 0; if (unlikely(cp->app != NULL)) { /* Some checks before mangling */ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) return 0; /* Call application helper if needed */ if (!ip_vs_app_pkt_out(cp, skb)) return 0; } tcph = (void *)skb_network_header(skb) + tcphoff; tcp_save_out_seq(skb, cp, tcph, tcphoff); tcph->source = cp->vport; tcph->dest = cp->cport; /* * for syn_ack * 1. adjust tcp opt mss in rs->client */ if (tcph->syn && tcph->ack) { tcp_opt_adjust_mss(cp->af, tcph); } /* adjust tcp ack/sack sequence */ if (tcp_out_adjust_seq(cp, tcph) == 0) { return 0; } /* * for syn_ack * 2. init sequence */ if (tcph->syn && tcph->ack) { tcp_out_init_seq(cp, tcph); } /* do csum later */ if (sysctl_ip_vs_csum_offload) { skb->csum_start = skb_network_header(skb) - skb->head + (ip_hdr(skb)->ihl << 2); skb->csum_offset = offsetof(struct tcphdr, check); skb->ip_summed = CHECKSUM_PARTIAL; } /* Adjust TCP checksums */ if (skb->ip_summed == CHECKSUM_PARTIAL) { tcp_partial_csum_reset(cp->af, (skb->len - tcphoff), tcph, &cp->vaddr, &cp->caddr); } else if (!cp->app) { /* Only port and addr are changed, do fast csum update */ tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, cp->dport, cp->vport); tcp_fast_csum_update(cp->af, tcph, &cp->laddr, &cp->caddr, cp->lport, cp->cport); if (skb->ip_summed == CHECKSUM_COMPLETE) skb->ip_summed = CHECKSUM_NONE; } else { /* full checksum calculation */ tcph->check = 0; skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6) tcph->check = csum_ipv6_magic(&cp->vaddr.in6, &cp->caddr.in6, skb->len - tcphoff, cp->protocol, skb->csum); else #endif tcph->check = csum_tcpudp_magic(cp->vaddr.ip, cp->caddr.ip, skb->len - tcphoff, cp->protocol, skb->csum); IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", pp->name, tcph->check, (char *)&(tcph->check) - (char *)tcph); } return 1; }
static void tarpit_tcp6(struct net *net, struct sk_buff *oldskb, unsigned int hook, unsigned int mode) { struct sk_buff *nskb; struct tcphdr *tcph, oth; unsigned int otcplen; int tcphoff; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); struct ipv6hdr *ip6h; const uint8_t tclass = 0; uint8_t proto; uint16_t payload; __be16 frag_off; proto = oip6h->nexthdr; tcphoff = ipv6_skip_exthdr(oldskb, (uint8_t *)(oip6h + 1) - oldskb->data, &proto, &frag_off); 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, &oth, sizeof(struct tcphdr))) { WARN_ON(1); 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; } nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb), GFP_ATOMIC); if (nskb == NULL) { if (net_ratelimit()) pr_debug("cannot alloc skb\n"); return; } /* This packet will not be the same as the other: clear nf fields */ nf_reset(nskb); skb_nfmark(nskb) = 0; skb_init_secmark(nskb); skb_shinfo(nskb)->gso_size = 0; skb_shinfo(nskb)->gso_segs = 0; skb_shinfo(nskb)->gso_type = 0; skb_put(nskb, sizeof(struct ipv6hdr)); ip6h = ipv6_hdr(nskb); *(__be32 *)ip6h = htonl(0x60000000 | (tclass << 20)); ip6h->nexthdr = IPPROTO_TCP; ip6h->saddr = oip6h->daddr; ip6h->daddr = oip6h->saddr; /* Adjust IP TTL */ if (mode == XTTARPIT_HONEYPOT) { ip6h->hop_limit = 128; } else { ip6h->hop_limit = ip6_dst_hoplimit(skb_dst(nskb)); } tcph = (struct tcphdr *)(skb_network_header(nskb) + sizeof(struct ipv6hdr)); /* Truncate to length (no data) */ skb_trim(nskb, sizeof(struct ipv6hdr) + sizeof(struct tcphdr)); tcph->doff = sizeof(struct tcphdr)/4; tcph->source = oth.dest; tcph->dest = oth.source; tcph->urg_ptr = 0; /* Reset flags */ ((uint8_t *)tcph)[13] = 0; payload = nskb->len - sizeof(struct ipv6hdr) - sizeof(struct tcphdr); if (!tarpit_generic(&oth, tcph, payload, mode)) goto free_nskb; ip6h->payload_len = htons(sizeof(struct tcphdr)); 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)); if (ip6_route_me_harder(net, nskb)) goto free_nskb; nskb->ip_summed = CHECKSUM_NONE; nf_ct_attach(nskb, oldskb); NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, nskb->sk, nskb, NULL, skb_dst(nskb)->dev, dst_output); return; free_nskb: kfree_skb(nskb); }
static unsigned int echo_tg6(struct sk_buff *oldskb, const struct xt_action_param *par) { const struct udphdr *oldudp; const struct ipv6hdr *oldip; struct udphdr *newudp, oldudp_buf; struct ipv6hdr *newip; struct sk_buff *newskb; unsigned int data_len; void *payload; struct flowi6 fl; struct dst_entry *dst = NULL; struct net *net = dev_net((par->in != NULL) ? par->in : par->out); /* This allows us to do the copy operation in fewer lines of code. */ if (skb_linearize(oldskb) < 0) return NF_DROP; oldip = ipv6_hdr(oldskb); oldudp = skb_header_pointer(oldskb, par->thoff, sizeof(*oldudp), &oldudp_buf); if (oldudp == NULL) return NF_DROP; if (ntohs(oldudp->len) <= sizeof(*oldudp)) return NF_DROP; newskb = alloc_skb(LL_MAX_HEADER + sizeof(*newip) + ntohs(oldudp->len), GFP_ATOMIC); if (newskb == NULL) return NF_DROP; skb_reserve(newskb, LL_MAX_HEADER); newskb->protocol = oldskb->protocol; skb_reset_network_header(newskb); newip = (void *)skb_put(newskb, sizeof(*newip)); newip->version = oldip->version; newip->priority = oldip->priority; memcpy(newip->flow_lbl, oldip->flow_lbl, sizeof(newip->flow_lbl)); newip->nexthdr = par->target->proto; newip->saddr = oldip->daddr; newip->daddr = oldip->saddr; skb_reset_transport_header(newskb); newudp = (void *)skb_put(newskb, sizeof(*newudp)); newudp->source = oldudp->dest; newudp->dest = oldudp->source; newudp->len = oldudp->len; data_len = htons(oldudp->len) - sizeof(*oldudp); payload = skb_header_pointer(oldskb, par->thoff + sizeof(*oldudp), data_len, NULL); memcpy(skb_put(newskb, data_len), payload, data_len); #if 0 /* * Since no fields are modified (we just swapped things around), * this works too in our specific echo case. */ newudp->check = oldudp->check; #else newudp->check = 0; newudp->check = csum_ipv6_magic(&newip->saddr, &newip->daddr, ntohs(newudp->len), IPPROTO_UDP, csum_partial(newudp, ntohs(newudp->len), 0)); #endif memset(&fl, 0, sizeof(fl)); fl.flowi6_proto = newip->nexthdr; memcpy(&fl.saddr, &newip->saddr, sizeof(fl.saddr)); memcpy(&fl.daddr, &newip->daddr, sizeof(fl.daddr)); fl.fl6_sport = newudp->source; fl.fl6_dport = newudp->dest; security_skb_classify_flow((struct sk_buff *)oldskb, flowi6_to_flowi(&fl)); dst = ip6_route_output(net, NULL, &fl); if (dst == NULL || dst->error != 0) { dst_release(dst); goto free_nskb; } skb_dst_set(newskb, dst); newip->hop_limit = ip6_dst_hoplimit(skb_dst(newskb)); newskb->ip_summed = CHECKSUM_NONE; /* "Never happens" (?) */ if (newskb->len > dst_mtu(skb_dst(newskb))) goto free_nskb; nf_ct_attach(newskb, oldskb); ip6_local_out(newskb); return NF_DROP; free_nskb: kfree_skb(newskb); return NF_DROP; }
static int translate(struct tn_translate *t) { struct tn_proto *p= t->ct->tuple.proto->hier[t->idx]; struct tcphdr *tcp; struct tcp_data *d; int len = 0; tcp = t->out->h.th = (struct tcphdr *)skb_put(t->out, sizeof(struct tcphdr)); d = (struct tcp_data *)&(t->ct->tuple.data[p->max_data - sizeof(struct tcp_data)]); if (t->dir == V4TOV6) { memcpy(tcp, t->pkt->data+t->pkt_ofs, sizeof(struct tcphdr)); if (t->ct->dir == t->dir) tcp->source = d->v6port[NAT_HOST]; else { tcp->dest = d->v6port[NAT_DEST]; } } else if (t->dir == V6TOV4) { void *start = find_ip6_hdr(t->pkt->nh.ipv6h, IPPROTO_TCP, 0, 1); if (start == NULL) return -EFAULT; memcpy(tcp, start, sizeof(struct tcphdr)); t->pkt_ofs = (unsigned)(start - VOID t->pkt->data); if (t->ct->dir == t->dir) tcp->source = d->v4port[NAT_HOST]; else { tcp->dest = d->v4port[NAT_DEST]; } len = t->pkt->len - (unsigned)(VOID start - VOID t->pkt); } else return -EINVAL; /* CHECK FOR DATA LENGTH!!!! */ t->pkt_ofs += sizeof(struct tcphdr); /* except for ports, everything in tcp header is the same, so it doesn't matter which we pass (the old or new) */ t->add_timeout = update_state(tcp, t->ct, t->dir, t->idx); if (t->idx < t->ct->tuple.proto->states - 1 && t->pkt_ofs < t->pkt->len) { /* we are not last */ int ret; t->idx++; ret = t->ct->tuple.proto->hier[t->idx]->translate(t); t->idx--; if (ret) return ret; } else { /* if we are last, just copy rest */ /* only most upper protocol sets timeout */ if (t->pkt->len > t->pkt_ofs) { unsigned int len = (unsigned int)(t->pkt->tail - (t->pkt->data + t->pkt_ofs)); memcpy(skb_put(t->out, len), VOID (t->pkt->data+t->pkt_ofs), len); t->pkt_ofs += len; } } tcp->check = 0; len = (unsigned int)(VOID t->out->tail - VOID(t->out->h.th)); /* now do the freaking checksum */ if (t->dir == V4TOV6) { struct ipv6hdr *ip6h = t->out->nh.ipv6h; /* rfc2460 says something about routing header options... DICK! */ tcp->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, len, IPPROTO_TCP, csum_partial((unsigned char *)tcp, len, 0)); } else { struct iphdr *iph = t->out->nh.iph; tcp->check = csum_tcpudp_magic(iph->saddr, iph->daddr, len, IPPROTO_TCP, csum_partial((unsigned char *)tcp, len, 0)); } return 0; }
void igmp6_send(struct in6_addr *addr, struct device *dev, int type) { struct sock *sk = igmp6_socket->sk; struct sk_buff *skb; struct icmp6hdr *hdr; struct inet6_ifaddr *ifp; struct in6_addr *snd_addr; struct in6_addr *addrp; struct in6_addr all_routers; int err, len, payload_len, full_len; u8 ra[8] = { IPPROTO_ICMPV6, 0, IPV6_TLV_ROUTERALERT, 0, 0, 0, IPV6_TLV_PADN, 0 }; snd_addr = addr; if (type == ICMPV6_MGM_REDUCTION) { snd_addr = &all_routers; ipv6_addr_all_routers(&all_routers); } len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); payload_len = len + sizeof(ra); full_len = sizeof(struct ipv6hdr) + payload_len; skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 0, 0, &err); if (skb == NULL) return; skb_reserve(skb, (dev->hard_header_len + 15) & ~15); if (dev->hard_header) { unsigned char ha[MAX_ADDR_LEN]; ndisc_mc_map(snd_addr, ha, dev, 1); dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len); } ifp = ipv6_get_lladdr(dev); if (ifp == NULL) { #if MCAST_DEBUG >= 1 printk(KERN_DEBUG "igmp6: %s no linklocal address\n", dev->name); #endif return; } ip6_nd_hdr(sk, skb, dev, &ifp->addr, snd_addr, NEXTHDR_HOP, payload_len); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr)); memset(hdr, 0, sizeof(struct icmp6hdr)); hdr->icmp6_type = type; addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr)); ipv6_addr_copy(addrp, addr); hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, snd_addr, len, IPPROTO_ICMPV6, csum_partial((__u8 *) hdr, len, 0)); dev_queue_xmit(skb); if (type == ICMPV6_MGM_REDUCTION) icmpv6_statistics.Icmp6OutGroupMembReductions++; else icmpv6_statistics.Icmp6OutGroupMembResponses++; icmpv6_statistics.Icmp6OutMsgs++; }
void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) { struct sock *sk = igmp6_socket->sk; struct sk_buff *skb; struct inet6_dev *idev; struct icmp6hdr *hdr; struct in6_addr *snd_addr; struct in6_addr *addrp; struct in6_addr addr_buf; struct in6_addr all_routers; int err, len, payload_len, full_len; u8 ra[8] = { IPPROTO_ICMPV6, 0, IPV6_TLV_ROUTERALERT, 2, 0, 0, IPV6_TLV_PADN, 0 }; #ifdef CONFIG_IPV6_MLD6_DEBUG char abuf[128]; in6_ntop(addr, abuf); MDBG3((KERN_DEBUG "igmp6_send(addr=%s, dev=%p(%s), type=%d)\n", abuf, dev, dev->name ? dev->name :"<null>", type)); #endif snd_addr = addr; if (type == ICMPV6_MGM_REDUCTION) { snd_addr = &all_routers; ipv6_addr_all_routers(&all_routers); } len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); payload_len = len + sizeof(ra); full_len = sizeof(struct ipv6hdr) + payload_len; skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 0, &err); if (skb == NULL) return; skb_reserve(skb, (dev->hard_header_len + 15) & ~15); if (dev->hard_header) { unsigned char ha[MAX_ADDR_LEN]; ndisc_mc_map(snd_addr, ha, dev, 1); if (dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len) < 0) goto out; } if (ipv6_get_lladdr(dev, &addr_buf)) { MDBG1((KERN_WARNING "igmp6: %s no linklocal address\n", dev->name)); goto out; } ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr)); memset(hdr, 0, sizeof(struct icmp6hdr)); hdr->icmp6_type = type; addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr)); ipv6_addr_copy(addrp, addr); hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len, IPPROTO_ICMPV6, csum_partial((__u8 *) hdr, len, 0)); dev_queue_xmit(skb); idev = in6_dev_get(dev); if (type == ICMPV6_MGM_REDUCTION) ICMP6_INC_STATS(idev,Icmp6OutGroupMembReductions); else ICMP6_INC_STATS(idev,Icmp6OutGroupMembResponses); ICMP6_INC_STATS(idev,Icmp6OutMsgs); if (idev) in6_dev_put(idev); return; out: kfree_skb(skb); }
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; 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("ip6t_REJECT: 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("ip6t_REJECT: Can't get TCP header.\n"); return; } otcplen = oldskb->len - tcphoff; if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { pr_debug("ip6t_REJECT: proto(%d) != IPPROTO_TCP, " "or too short. otcplen = %d\n", proto, otcplen); return; } if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) BUG(); if (otcph.rst) { pr_debug("ip6t_REJECT: RST is set\n"); return; } if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, skb_checksum(oldskb, tcphoff, otcplen, 0))) { pr_debug("ip6t_REJECT: 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) return; if (dst->error || 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()) printk("ip6t_REJECT: Can't 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); ip6h->version = 6; 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)); 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; } ((u_int8_t *)tcph)[13] = 0; tcph->rst = 1; tcph->ack = needs_ack; tcph->window = 0; tcph->urg_ptr = 0; tcph->check = 0; 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 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; __be16 frag_off; struct flowi6 fl6; 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, &frag_off); 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(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_TCP; fl6.saddr = oip6h->daddr; fl6.daddr = oip6h->saddr; fl6.fl6_sport = otcph.dest; fl6.fl6_dport = otcph.source; security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); dst = ip6_route_output(net, NULL, &fl6); if (dst == NULL || dst->error) { dst_release(dst); return; } dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); if (IS_ERR(dst)) 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) { net_dbg_ratelimited("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); ip6_flow_hdr(ip6h, tclass, 0); ip6h->hop_limit = ip6_dst_hoplimit(dst); ip6h->nexthdr = IPPROTO_TCP; ip6h->saddr = oip6h->daddr; ip6h->daddr = oip6h->saddr; 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 = 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); }
int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int hooknum, unsigned int hdrlen) { struct { struct icmp6hdr icmp6; struct ipv6hdr ip6; } *inside; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); enum nf_nat_manip_type manip = HOOK2MANIP(hooknum); const struct nf_nat_l4proto *l4proto; struct nf_conntrack_tuple target; unsigned long statusbit; NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY); if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) return 0; if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6)) return 0; inside = (void *)skb->data + hdrlen; if (inside->icmp6.icmp6_type == NDISC_REDIRECT) { if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) return 0; if (ct->status & IPS_NAT_MASK) return 0; } if (manip == NF_NAT_MANIP_SRC) statusbit = IPS_SRC_NAT; else statusbit = IPS_DST_NAT; /* Invert if this is reply direction */ if (dir == IP_CT_DIR_REPLY) statusbit ^= IPS_NAT_MASK; if (!(ct->status & statusbit)) return 1; l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, inside->ip6.nexthdr); if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6), l4proto, &ct->tuplehash[!dir].tuple, !manip)) return 0; if (skb->ip_summed != CHECKSUM_PARTIAL) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); inside = (void *)skb->data + hdrlen; inside->icmp6.icmp6_cksum = 0; inside->icmp6.icmp6_cksum = csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len - hdrlen, IPPROTO_ICMPV6, csum_partial(&inside->icmp6, skb->len - hdrlen, 0)); } nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, IPPROTO_ICMPV6); if (!nf_nat_ipv6_manip_pkt(skb, 0, l4proto, &target, manip)) return 0; return 1; }
int ip6_route_me_harder(struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); const struct ipv6hdr *iph = ipv6_hdr(skb); struct dst_entry *dst; struct flowi6 fl6 = { .flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0, .flowi6_mark = skb->mark, .daddr = iph->daddr, .saddr = iph->saddr, }; dst = ip6_route_output(net, skb->sk, &fl6); if (dst->error) { IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n"); dst_release(dst); return -EINVAL; } /* Drop old route. */ skb_dst_drop(skb); skb_dst_set(skb, dst); #ifdef CONFIG_XFRM if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && xfrm_decode_session(skb, flowi6_to_flowi(&fl6), AF_INET6) == 0) { skb_dst_set(skb, NULL); dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), skb->sk, 0); if (IS_ERR(dst)) return -1; skb_dst_set(skb, dst); } #endif return 0; } EXPORT_SYMBOL(ip6_route_me_harder); /* * Extra routing may needed on local out, as the QUEUE target never * returns control to the table. */ struct ip6_rt_info { struct in6_addr daddr; struct in6_addr saddr; u_int32_t mark; }; static void nf_ip6_saveroute(const struct sk_buff *skb, struct nf_queue_entry *entry) { struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry); if (entry->hook == NF_INET_LOCAL_OUT) { const struct ipv6hdr *iph = ipv6_hdr(skb); rt_info->daddr = iph->daddr; rt_info->saddr = iph->saddr; rt_info->mark = skb->mark; } } static int nf_ip6_reroute(struct sk_buff *skb, const struct nf_queue_entry *entry) { struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry); if (entry->hook == NF_INET_LOCAL_OUT) { const struct ipv6hdr *iph = ipv6_hdr(skb); if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) || skb->mark != rt_info->mark) return ip6_route_me_harder(skb); } return 0; } static int nf_ip6_route(struct net *net, struct dst_entry **dst, struct flowi *fl, bool strict) { static const struct ipv6_pinfo fake_pinfo; static const struct inet_sock fake_sk = { /* makes ip6_route_output set RT6_LOOKUP_F_IFACE: */ .sk.sk_bound_dev_if = 1, .pinet6 = (struct ipv6_pinfo *) &fake_pinfo, }; const void *sk = strict ? &fake_sk : NULL; struct dst_entry *result; int err; result = ip6_route_output(net, sk, &fl->u.ip6); err = result->error; if (err) dst_release(result); else *dst = result; return err; } __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, u_int8_t protocol) { const struct ipv6hdr *ip6h = ipv6_hdr(skb); __sum16 csum = 0; switch (skb->ip_summed) { case CHECKSUM_COMPLETE: if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN) break; if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len - dataoff, protocol, csum_sub(skb->csum, skb_checksum(skb, 0, dataoff, 0)))) { skb->ip_summed = CHECKSUM_UNNECESSARY; break; } /* fall through */ case CHECKSUM_NONE: skb->csum = ~csum_unfold( csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len - dataoff, protocol, csum_sub(0, skb_checksum(skb, 0, dataoff, 0)))); csum = __skb_checksum_complete(skb); } return csum; } EXPORT_SYMBOL(nf_ip6_checksum); static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, unsigned int len, u_int8_t protocol) { const struct ipv6hdr *ip6h = ipv6_hdr(skb); __wsum hsum; __sum16 csum = 0; switch (skb->ip_summed) { case CHECKSUM_COMPLETE: if (len == skb->len - dataoff) return nf_ip6_checksum(skb, hook, dataoff, protocol); /* fall through */ case CHECKSUM_NONE: hsum = skb_checksum(skb, 0, dataoff, 0); skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len - dataoff, protocol, csum_sub(0, hsum))); skb->ip_summed = CHECKSUM_NONE; return __skb_checksum_complete_head(skb, dataoff + len); } return csum; }; static const struct nf_afinfo nf_ip6_afinfo = { .family = AF_INET6, .checksum = nf_ip6_checksum, .checksum_partial = nf_ip6_checksum_partial, .route = nf_ip6_route, .saveroute = nf_ip6_saveroute, .reroute = nf_ip6_reroute, .route_key_size = sizeof(struct ip6_rt_info), }; int __init ipv6_netfilter_init(void) { return nf_register_afinfo(&nf_ip6_afinfo); } /* This can be called from inet6_init() on errors, so it cannot * be marked __exit. -DaveM */ void ipv6_netfilter_fini(void) { nf_unregister_afinfo(&nf_ip6_afinfo); }
static int tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, struct ip_vs_iphdr *iph) { struct tcphdr *tcph; unsigned int tcphoff = iph->len; int oldlen; int payload_csum = 0; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6 && iph->fragoffs) return 1; #endif oldlen = skb->len - tcphoff; /* csum_check requires unshared skb */ if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) return 0; if (unlikely(cp->app != NULL)) { int ret; /* Some checks before mangling */ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) return 0; /* * Attempt ip_vs_app call. * It will fix ip_vs_conn and iph ack_seq stuff */ if (!(ret = ip_vs_app_pkt_in(cp, skb))) return 0; /* ret=2: csum update is needed after payload mangling */ if (ret == 1) oldlen = skb->len - tcphoff; else payload_csum = 1; } tcph = (void *)skb_network_header(skb) + tcphoff; tcph->dest = cp->dport; /* * Adjust TCP checksums */ if (skb->ip_summed == CHECKSUM_PARTIAL) { tcp_partial_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, htons(oldlen), htons(skb->len - tcphoff)); } else if (!payload_csum) { /* Only port and addr are changed, do fast csum update */ tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, cp->vport, cp->dport); if (skb->ip_summed == CHECKSUM_COMPLETE) skb->ip_summed = (cp->app && pp->csum_check) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE; } else { /* full checksum calculation */ tcph->check = 0; skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6) tcph->check = csum_ipv6_magic(&cp->caddr.in6, &cp->daddr.in6, skb->len - tcphoff, cp->protocol, skb->csum); else #endif tcph->check = csum_tcpudp_magic(cp->caddr.ip, cp->daddr.ip, skb->len - tcphoff, cp->protocol, skb->csum); skb->ip_summed = CHECKSUM_UNNECESSARY; } return 1; }
#ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6) tcph->check = csum_ipv6_magic(&cp->vaddr.in6, &cp->caddr.in6, skb->len - tcphoff, cp->protocol, skb->csum); else #endif tcph->check = csum_tcpudp_magic(cp->vaddr.ip, cp->caddr.ip, skb->len - tcphoff, cp->protocol, skb->csum);
static int ndisc_send_unspec(int oif, const struct in6_addr *dest, uint8_t *hdr, int hdrlen, struct iovec *optv, size_t optvlen) { struct { struct ip6_hdr ip; struct icmp6_hdr icmp; uint8_t data[1500]; } frame; struct msghdr msgh; struct cmsghdr *cmsg; struct in6_pktinfo *pinfo; struct sockaddr_in6 dst; char cbuf[CMSG_SPACE(sizeof(*pinfo))]; struct iovec iov; uint8_t *data = (uint8_t *)(&frame.icmp); int type, fd, ret, remlen, datalen = 0, written = 0, v = 1; if (hdr == NULL || hdrlen < 0 || (size_t)hdrlen < sizeof(struct icmp6_hdr) || (size_t)hdrlen > (sizeof(frame) - sizeof(struct ip6_hdr))) return -EINVAL; if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)) < 0) return -1; if (setsockopt(fd, IPPROTO_IPV6, IP_HDRINCL, &v, sizeof(v)) < 0) { dbg("cannot set IP_HDRINCL: %s\n", strerror(errno)); close(fd); return -errno; } memset(&frame, 0, sizeof(frame)); memset(&dst, 0, sizeof(dst)); dst.sin6_addr = *dest; /* Copy ICMPv6 header and update various length values */ memcpy(data, hdr, hdrlen); data += hdrlen; datalen += hdrlen; remlen = sizeof(frame) - sizeof(struct ip6_hdr) - hdrlen; /* Prepare for csum: write trailing options by linearizing iov */ if ((iov_linearize(data, remlen, optv, optvlen, &written) != 0) || (written & 0x1)) /* Ensure length is even for csum() */ return -1; datalen += written; /* Fill in the IPv6 header */ frame.ip.ip6_vfc = 0x60; frame.ip.ip6_plen = htons(datalen); frame.ip.ip6_nxt = IPPROTO_ICMPV6; frame.ip.ip6_hlim = 255; frame.ip.ip6_dst = *dest; /* all other fields are already set to zero */ //frame.icmp.icmp6_cksum = in6_cksum(&in6addr_any, dest, &frame.icmp, // datalen, IPPROTO_ICMPV6); frame.icmp.icmp6_cksum = csum_ipv6_magic(&in6addr_any, dest, datalen, IPPROTO_ICMPV6, csum_partial(&frame.icmp, datalen, 0)); iov.iov_base = &frame; iov.iov_len = sizeof(frame.ip) + datalen; dst.sin6_family = AF_INET6; msgh.msg_name = &dst; msgh.msg_namelen = sizeof(dst); msgh.msg_iov = &iov; msgh.msg_iovlen = 1; msgh.msg_flags = 0; memset(cbuf, 0, CMSG_SPACE(sizeof(*pinfo))); cmsg = (struct cmsghdr *)cbuf; pinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); pinfo->ipi6_ifindex = oif; cmsg->cmsg_len = CMSG_LEN(sizeof(*pinfo)); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; msgh.msg_control = cmsg; msgh.msg_controllen = cmsg->cmsg_len; ret = sendmsg(fd, &msgh, 0); if (ret < 0) dbg("sendmsg: if index %u dest %x:%x:%x:%x:%x:%x:%x:%x: %s\n", oif, NIP6ADDR(dest), strerror(errno)); close(fd); type = hdr[0]; if (type == ND_NEIGHBOR_SOLICIT) { statistics_inc(&mipl_stat, MIPL_STATISTICS_OUT_NS_UNSPEC); } else if (type == ND_ROUTER_SOLICIT) { statistics_inc(&mipl_stat, MIPL_STATISTICS_OUT_RS_UNSPEC); } return ret; }