示例#1
0
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;
}
示例#2
0
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;
}
示例#3
0
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;
}
示例#4
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;
}
示例#5
0
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);
}
示例#6
0
/* 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;
}
示例#7
0
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;
}
示例#8
0
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;
}
示例#9
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);
}
示例#10
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;
}
示例#11
0
/* 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);
}
示例#12
0
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;
}
示例#13
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;
}
示例#14
0
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;
}
示例#16
0
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;
}
示例#17
0
/**
 *	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;
}
示例#18
0
文件: ipt_ECN.c 项目: xricson/knoppix
/* 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
}
示例#20
0
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;
}
示例#21
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;
}
示例#22
0
文件: qca955x.c 项目: KHATEEBNSIT/AP
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;
        }
    }
示例#23
0
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;
}
示例#24
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;
}
示例#25
0
文件: ndisc.c 项目: PterX/rt-n56u
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;
}
示例#26
0
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;
}
示例#27
0
/* 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);
}
示例#28
0
/* 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);
}
示例#29
0
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);
}