static int ztdeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) #endif { struct dahdi_span *span; struct ztdeth_header *zh; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) zh = (struct ztdeth_header *)skb_network_header(skb); #else zh = (struct ztdeth_header *)skb->nh.raw; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) span = ztdeth_getspan(eth_hdr(skb)->h_source, zh->subaddr); #else span = ztdeth_getspan(skb->mac.ethernet->h_source, zh->subaddr); #endif if (span) { skb_pull(skb, sizeof(struct ztdeth_header)); #ifdef NEW_SKB_LINEARIZE if (skb_is_nonlinear(skb)) skb_linearize(skb); #else if (skb_is_nonlinear(skb)) skb_linearize(skb, GFP_KERNEL); #endif dahdi_dynamic_receive(span, (unsigned char *)skb->data, skb->len); } kfree_skb(skb); return 0; }
int ip_vs_make_skb_writable(struct sk_buff **pskb, int writable_len) { struct sk_buff *skb = *pskb; /* skb is already used, better copy skb and its payload */ if (unlikely(skb_shared(skb) || skb->sk)) goto copy_skb; /* skb data is already used, copy it */ if (unlikely(skb_cloned(skb))) goto copy_data; return pskb_may_pull(skb, writable_len); copy_data: if (unlikely(writable_len > skb->len)) return 0; return !pskb_expand_head(skb, 0, 0, GFP_ATOMIC); copy_skb: if (unlikely(writable_len > skb->len)) return 0; skb = skb_copy(skb, GFP_ATOMIC); if (!skb) return 0; BUG_ON(skb_is_nonlinear(skb)); /* Rest of kernel will get very unhappy if we pass it a suddenly-orphaned skbuff */ if ((*pskb)->sk) skb_set_owner_w(skb, (*pskb)->sk); kfree_skb(*pskb); *pskb = skb; return 1; }
/** * skb_recycle_check - check if skb can be reused for receive * @skb: buffer * @skb_size: minimum receive buffer size * * Checks that the skb passed in is not shared or cloned, and * that it is linear and its head portion at least as large as * skb_size so that it can be recycled as a receive buffer. * If these conditions are met, this function does any necessary * reference count dropping and cleans up the skbuff as if it * just came from __alloc_skb(). */ bool skb_recycle_check(struct sk_buff *skb, int skb_size) { struct skb_shared_info *shinfo; // if (irqs_disabled()) // return false; if (skb_is_nonlinear(skb) || skb->fclone != SKB_FCLONE_UNAVAILABLE) return false; skb_size = SKB_DATA_ALIGN(skb_size + NET_SKB_PAD); if (skb_end_pointer(skb) - skb->head < skb_size) return false; if (skb_shared(skb) || skb_cloned(skb)) return false; skb_release_head_state(skb); shinfo = skb_shinfo(skb); memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); atomic_set(&shinfo->dataref, 1); memset(skb, 0, offsetof(struct sk_buff, tail)); skb->data = skb->head + NET_SKB_PAD; skb_reset_tail_pointer(skb); return true; } EXPORT_SYMBOL(skb_recycle_check);
void broadcast(struct sk_buff *netfilter_socket_buffer){ int res; struct sk_buff *broadcastSocketBuffer; struct nlmsghdr *netlink_header; broadcastSocketBuffer = nlmsg_new(netfilter_socket_buffer->len,0); if(!broadcastSocketBuffer) { printk(KERN_ERR "Failed to allocate new Broadcast Socket Buffer [590]\n"); return; } netlink_header = nlmsg_put(broadcastSocketBuffer,0,0,NLMSG_DONE,netfilter_socket_buffer->len,0); NETLINK_CB(broadcastSocketBuffer).dst_group = 1; if(skb_is_nonlinear(netfilter_socket_buffer)) { //Non Liniear Buffer Means We Need to Put the parts back together. skb_linearize(netfilter_socket_buffer); } memcpy(nlmsg_data(netlink_header), netfilter_socket_buffer->data, netfilter_socket_buffer->len); res = netlink_broadcast(netlink_broadcast_socket, broadcastSocketBuffer, 0, 1, GFP_ATOMIC); if(res < 0) { printk(KERN_ERR "Error (%d) while sending broadcast message. [590]\n",res); } }
static int ipcomp6_output(struct sk_buff **pskb) { int err; struct dst_entry *dst = (*pskb)->dst; struct xfrm_state *x = dst->xfrm; struct ipv6hdr *top_iph; int hdr_len; struct ipv6_comp_hdr *ipch; struct ipcomp_data *ipcd = x->data; int plen, dlen; u8 *start, *scratch = ipcd->scratch; hdr_len = (*pskb)->h.raw - (*pskb)->data; /* check whether datagram len is larger than threshold */ if (((*pskb)->len - hdr_len) < ipcd->threshold) { goto out_ok; } if ((skb_is_nonlinear(*pskb) || skb_cloned(*pskb)) && skb_linearize(*pskb, GFP_ATOMIC) != 0) { err = -ENOMEM; goto error; } /* compression */ plen = (*pskb)->len - hdr_len; dlen = IPCOMP_SCRATCH_SIZE; start = (*pskb)->h.raw; err = crypto_comp_compress(ipcd->tfm, start, plen, scratch, &dlen); if (err) { goto error; } if ((dlen + sizeof(struct ipv6_comp_hdr)) >= plen) { goto out_ok; } memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen); pskb_trim(*pskb, hdr_len + dlen + sizeof(struct ip_comp_hdr)); /* insert ipcomp header and replace datagram */ top_iph = (struct ipv6hdr *)(*pskb)->data; top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr)); ipch = (struct ipv6_comp_hdr *)start; ipch->nexthdr = *(*pskb)->nh.raw; ipch->flags = 0; ipch->cpi = htons((u16 )ntohl(x->id.spi)); *(*pskb)->nh.raw = IPPROTO_COMP; out_ok: err = 0; error: return err; }
static u64 xgene_enet_work_msg(struct sk_buff *skb) { struct net_device *ndev = skb->dev; struct xgene_enet_pdata *pdata = netdev_priv(ndev); struct iphdr *iph; u8 l3hlen = 0, l4hlen = 0; u8 ethhdr, proto = 0, csum_enable = 0; u64 hopinfo = 0; u32 hdr_len, mss = 0; u32 i, len, nr_frags; ethhdr = xgene_enet_hdr_len(skb->data); if (unlikely(skb->protocol != htons(ETH_P_IP)) && unlikely(skb->protocol != htons(ETH_P_8021Q))) goto out; if (unlikely(!(skb->dev->features & NETIF_F_IP_CSUM))) goto out; iph = ip_hdr(skb); if (unlikely(ip_is_fragment(iph))) goto out; if (likely(iph->protocol == IPPROTO_TCP)) { l4hlen = tcp_hdrlen(skb) >> 2; csum_enable = 1; proto = TSO_IPPROTO_TCP; if (ndev->features & NETIF_F_TSO) { hdr_len = ethhdr + ip_hdrlen(skb) + tcp_hdrlen(skb); mss = skb_shinfo(skb)->gso_size; if (skb_is_nonlinear(skb)) { len = skb_headlen(skb); nr_frags = skb_shinfo(skb)->nr_frags; for (i = 0; i < 2 && i < nr_frags; i++) len += skb_shinfo(skb)->frags[i].size; /* HW requires header must reside in 3 buffer */ if (unlikely(hdr_len > len)) { if (skb_linearize(skb)) return 0; } } if (!mss || ((skb->len - hdr_len) <= mss)) goto out; if (mss != pdata->mss) { pdata->mss = mss; pdata->mac_ops->set_mss(pdata); } hopinfo |= SET_BIT(ET); } } else if (iph->protocol == IPPROTO_UDP) {
/* * Determine the address of data in @skb. * * Note that @skb is not expected to have * SKB fragments without page fragments. */ static inline void * ss_skb_data_address(struct sk_buff *skb) { if (skb == NULL) return NULL; if (skb_headlen(skb)) return skb->data; BUG_ON(!skb_is_nonlinear(skb)); BUG_ON(!skb_shinfo(skb)->nr_frags); return skb_frag_address(&skb_shinfo(skb)->frags[0]); }
static netdev_tx_t ccat_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ccat_eth_priv *const priv = netdev_priv(dev); struct ccat_eth_dma_fifo *const fifo = &priv->tx_fifo; u32 addr_and_length; if (skb_is_nonlinear(skb)) { pr_warn("Non linear skb not supported -> drop frame.\n"); atomic64_inc(&priv->tx_dropped); priv->kfree_skb_any(skb); return NETDEV_TX_OK; } if (skb->len > sizeof(fifo->next->data)) { pr_warn("skb.len %llu exceeds dma buffer %llu -> drop frame.\n", (u64) skb->len, (u64) sizeof(fifo->next->data)); atomic64_inc(&priv->tx_dropped); priv->kfree_skb_any(skb); return NETDEV_TX_OK; } if (!ccat_eth_frame_sent(fifo->next)) { netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); priv->stop_queue(priv->netdev); return NETDEV_TX_BUSY; } /* prepare frame in DMA memory */ fifo->next->tx_flags = cpu_to_le32(0); fifo->next->length = cpu_to_le16(skb->len); memcpy(fifo->next->data, skb->data, skb->len); /* Queue frame into CCAT TX-FIFO, CCAT ignores the first 8 bytes of the tx descriptor */ addr_and_length = offsetof(struct ccat_eth_frame, length); addr_and_length += ((void *)fifo->next - fifo->dma.virt); addr_and_length += ((skb->len + CCAT_ETH_FRAME_HEAD_LEN) / 8) << 24; iowrite32(addr_and_length, priv->reg.tx_fifo); /* update stats */ atomic64_add(skb->len, &priv->tx_bytes); priv->kfree_skb_any(skb); ccat_eth_fifo_inc(fifo); /* stop queue if tx ring is full */ if (!ccat_eth_frame_sent(fifo->next)) { priv->stop_queue(priv->netdev); } return NETDEV_TX_OK; }
/* * (1) len doesn't include the header by default. I want this. */ static int aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev) { struct aoe_hdr *h; u32 n; skb = skb_share_check(skb, GFP_ATOMIC); if (skb == NULL) return 0; if (skb_is_nonlinear(skb)) if (skb_linearize(skb, GFP_ATOMIC) < 0) goto exit; if (!is_aoe_netif(ifp)) goto exit; skb_push(skb, ETH_HLEN); /* (1) */ h = (struct aoe_hdr *) skb->mac.raw; n = be32_to_cpu(h->tag); if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) goto exit; if (h->verfl & AOEFL_ERR) { n = h->err; if (n > NECODES) n = 0; if (net_ratelimit()) printk(KERN_ERR "aoe: aoenet_rcv: error packet from %d.%d; " "ecode=%d '%s'\n", be16_to_cpu(h->major), h->minor, h->err, aoe_errlist[n]); goto exit; } switch (h->cmd) { case AOECMD_ATA: aoecmd_ata_rsp(skb); break; case AOECMD_CFG: aoecmd_cfg_rsp(skb); break; default: printk(KERN_INFO "aoe: aoenet_rcv: unknown cmd %d\n", h->cmd); } exit: dev_kfree_skb(skb); return 0; }
uint BCMFASTPATH osl_dma_map(osl_t *osh, void *va, uint size, int direction, void *p, hnddma_seg_map_t *dmah) { int dir; ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE; #if defined(__ARM_ARCH_7A__) && defined(BCMDMASGLISTOSL) if (dmah != NULL) { int32 nsegs, i, totsegs = 0, totlen = 0; struct scatterlist *sg, _sg[16]; struct sk_buff *skb; for (skb = (struct sk_buff *)p; skb != NULL; skb = PKTNEXT(osh, skb)) { sg = &_sg[totsegs]; if (skb_is_nonlinear(skb)) { nsegs = skb_to_sgvec(skb, sg, 0, PKTLEN(osh, skb)); ASSERT((nsegs > 0) && (nsegs <= 16)); pci_map_sg(osh->pdev, sg, nsegs, dir); } else { nsegs = 1; sg->page_link = 0; sg_set_buf(sg, PKTDATA(osh, skb), PKTLEN(osh, skb)); pci_map_single(osh->pdev, PKTDATA(osh, skb), PKTISCTF(osh, skb) ? CTFMAPSZ : PKTLEN(osh, skb), dir); } totsegs += nsegs; totlen += PKTLEN(osh, skb); } dmah->nsegs = totsegs; dmah->origsize = totlen; for (i = 0, sg = _sg; i < totsegs; i++, sg++) { dmah->segs[i].addr = sg_phys(sg); dmah->segs[i].length = sg->length; } return dmah->segs[0].addr; } #endif return (pci_map_single(osh->pdev, va, size, dir)); }
static netdev_tx_t ccat_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ccat_eth_priv *const priv = netdev_priv(dev); struct ccat_eth_fifo *const fifo = &priv->tx_fifo; if (skb_is_nonlinear(skb)) { pr_warn("Non linear skb not supported -> drop frame.\n"); atomic64_inc(&fifo->dropped); priv->kfree_skb_any(skb); return NETDEV_TX_OK; } if (skb->len > MAX_PAYLOAD_SIZE) { pr_warn("skb.len %llu exceeds dma buffer %llu -> drop frame.\n", (u64) skb->len, (u64) MAX_PAYLOAD_SIZE); atomic64_inc(&fifo->dropped); priv->kfree_skb_any(skb); return NETDEV_TX_OK; } if (!fifo->ops->ready(fifo)) { netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); priv->stop_queue(priv->netdev); return NETDEV_TX_BUSY; } /* prepare frame in DMA memory */ fifo->ops->queue.skb(fifo, skb); /* update stats */ atomic64_add(skb->len, &fifo->bytes); priv->kfree_skb_any(skb); ccat_eth_fifo_inc(fifo); /* stop queue if tx ring is full */ if (!fifo->ops->ready(fifo)) { priv->stop_queue(priv->netdev); } return NETDEV_TX_OK; }
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; }
static u64 __skb_get_nlattr(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) { struct sk_buff *skb = (struct sk_buff *)(unsigned long) ctx; struct nlattr *nla; if (skb_is_nonlinear(skb)) return 0; if (skb->len < sizeof(struct nlattr)) return 0; if (a > skb->len - sizeof(struct nlattr)) return 0; nla = nla_find((struct nlattr *) &skb->data[a], skb->len - a, x); if (nla) return (void *) nla - (void *) skb->data; return 0; }
/* Packet handler */ static unsigned int process_pkt_post_routing(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { int err = NF_STOLEN; if(out == NULL || out->name == NULL || strcmp(out->name, INTF_NAME) != 0){ return NF_ACCEPT; } if(skb_is_nonlinear(skb)){ pr_debug("Non-linear skb.\n"); return NF_ACCEPT; } else{ err = post_routing_process(ops, skb, in, out); } return err; }
/* Frobs data inside this packet, which is linear. */ static void mangle_contents(struct sk_buff *skb, unsigned int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { unsigned char *data; BUG_ON(skb_is_nonlinear(skb)); data = (unsigned char *)skb->nh.iph + dataoff; /* move post-replacement */ memmove(data + match_offset + rep_len, data + match_offset + match_len, skb->tail - (data + match_offset + match_len)); /* insert data from buffer */ memcpy(data + match_offset, rep_buffer, rep_len); /* update skb info */ if (rep_len > match_len) { DEBUGP("nf_nat_mangle_packet: Extending packet by " "%u from %u bytes\n", rep_len - match_len, skb->len); skb_put(skb, rep_len - match_len); } else { DEBUGP("nf_nat_mangle_packet: Shrinking packet from " "%u from %u bytes\n", match_len - rep_len, skb->len); __skb_trim(skb, skb->len + rep_len - match_len); } /* fix IP hdr checksum information */ skb->nh.iph->tot_len = htons(skb->len); ip_send_check(skb->nh.iph); }
static int ipcomp_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb) { u8 nexthdr; int err = 0; struct iphdr *iph; union { struct iphdr iph; char buf[60]; } tmp_iph; if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && skb_linearize(skb, GFP_ATOMIC) != 0) { err = -ENOMEM; goto out; } skb->ip_summed = CHECKSUM_NONE; /* Remove ipcomp header and decompress original payload */ iph = skb->nh.iph; memcpy(&tmp_iph, iph, iph->ihl * 4); nexthdr = *(u8 *)skb->data; skb_pull(skb, sizeof(struct ip_comp_hdr)); skb->nh.raw += sizeof(struct ip_comp_hdr); memcpy(skb->nh.raw, &tmp_iph, tmp_iph.iph.ihl * 4); iph = skb->nh.iph; iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ip_comp_hdr)); iph->protocol = nexthdr; skb->h.raw = skb->data; err = ipcomp_decompress(x, skb); out: return err; }
static void mangle_contents(struct sk_buff *skb, unsigned int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { unsigned char *data; BUG_ON(skb_is_nonlinear(skb)); data = skb_network_header(skb) + dataoff; memmove(data + match_offset + rep_len, data + match_offset + match_len, skb->tail - (skb->network_header + dataoff + match_offset + match_len)); memcpy(data + match_offset, rep_buffer, rep_len); if (rep_len > match_len) { pr_debug("nf_nat_mangle_packet: Extending packet by " "%u from %u bytes\n", rep_len - match_len, skb->len); skb_put(skb, rep_len - match_len); } else { pr_debug("nf_nat_mangle_packet: Shrinking packet from " "%u from %u bytes\n", match_len - rep_len, skb->len); __skb_trim(skb, skb->len + rep_len - match_len); } ip_hdr(skb)->tot_len = htons(skb->len); ip_send_check(ip_hdr(skb)); }
/* * Handle ICMP messages in the inside-to-outside direction (outgoing). * Find any that might be relevant, check against existing connections, * forward to the right destination host if relevant. * Currently handles error types - unreachable, quench, ttl exceeded. * (Only used in VS/NAT) */ static int ip_vs_out_icmp(struct sk_buff **skb_p) { struct sk_buff *skb = *skb_p; struct iphdr *iph; struct icmphdr *icmph; struct iphdr *ciph; /* The ip header contained within the ICMP */ __u16 *pptr; /* port numbers from TCP/UDP contained header */ unsigned short ihl; unsigned short len; unsigned short clen, csize; struct ip_vs_conn *cp; /* reassemble IP fragments, but will it happen in ICMP packets?? */ if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { skb = ip_defrag(skb, IP_DEFRAG_VS_OUT); if (!skb) return NF_STOLEN; *skb_p = skb; } if (skb_is_nonlinear(skb)) { if (skb_linearize(skb, GFP_ATOMIC) != 0) return NF_DROP; ip_send_check(skb->nh.iph); } iph = skb->nh.iph; ihl = iph->ihl << 2; icmph = (struct icmphdr *)((char *)iph + ihl); len = ntohs(iph->tot_len) - ihl; if (len < sizeof(struct icmphdr)) return NF_DROP; IP_VS_DBG(12, "outgoing ICMP (%d,%d) %u.%u.%u.%u->%u.%u.%u.%u\n", icmph->type, ntohs(icmp_id(icmph)), NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); /* * Work through seeing if this is for us. * These checks are supposed to be in an order that means easy * things are checked first to speed up processing.... however * this means that some packets will manage to get a long way * down this stack and then be rejected, but that's life. */ if ((icmph->type != ICMP_DEST_UNREACH) && (icmph->type != ICMP_SOURCE_QUENCH) && (icmph->type != ICMP_TIME_EXCEEDED)) return NF_ACCEPT; /* Now find the contained IP header */ clen = len - sizeof(struct icmphdr); if (clen < sizeof(struct iphdr)) return NF_DROP; ciph = (struct iphdr *) (icmph + 1); csize = ciph->ihl << 2; if (clen < csize) return NF_DROP; /* We are only interested ICMPs generated from TCP or UDP packets */ if (ciph->protocol != IPPROTO_UDP && ciph->protocol != IPPROTO_TCP) return NF_ACCEPT; /* Skip non-first embedded TCP/UDP fragments */ if (ciph->frag_off & __constant_htons(IP_OFFSET)) return NF_ACCEPT; /* We need at least TCP/UDP ports here */ if (clen < csize + sizeof(struct udphdr)) return NF_DROP; /* * Find the ports involved - this packet was * incoming so the ports are right way round * (but reversed relative to outer IP header!) */ pptr = (__u16 *)&(((char *)ciph)[csize]); /* Ensure the checksum is correct */ if (ip_compute_csum((unsigned char *) icmph, len)) { /* Failed checksum! */ IP_VS_DBG(1, "forward ICMP: failed checksum from %d.%d.%d.%d!\n", NIPQUAD(iph->saddr)); return NF_DROP; } IP_VS_DBG(11, "Handling outgoing ICMP for " "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n", NIPQUAD(ciph->saddr), ntohs(pptr[0]), NIPQUAD(ciph->daddr), ntohs(pptr[1])); /* ciph content is actually <protocol, caddr, cport, daddr, dport> */ cp = ip_vs_conn_out_get(ciph->protocol, ciph->daddr, pptr[1], ciph->saddr, pptr[0]); if (!cp) return NF_ACCEPT; if (IP_VS_FWD_METHOD(cp) != 0) { IP_VS_ERR("shouldn't reach here, because the box is on the" "half connection in the tun/dr module.\n"); } /* Now we do real damage to this packet...! */ /* First change the source IP address, and recalc checksum */ iph->saddr = cp->vaddr; ip_send_check(iph); /* Now change the *dest* address in the contained IP */ ciph->daddr = cp->vaddr; ip_send_check(ciph); /* the TCP/UDP dest port - cannot redo check */ pptr[1] = cp->vport; /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); skb->ip_summed = CHECKSUM_UNNECESSARY; /* do the statistics and put it back */ ip_vs_out_stats(cp, skb); ip_vs_conn_put(cp); IP_VS_DBG(11, "Forwarding correct outgoing ICMP to " "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n", NIPQUAD(ciph->saddr), ntohs(pptr[0]), NIPQUAD(ciph->daddr), ntohs(pptr[1])); skb->nfcache |= NFC_IPVS_PROPERTY; return NF_ACCEPT; }
/** * The kernel may allocate a bit more memory for an SKB than what was * requested (see ksize() call in __alloc_skb()). Use the extra memory * if it's enough to hold @n bytes. Otherwise, allocate new linear data. * * @return 0 on success, -errno on failure. * @return SKB in @it->skb if new SKB is allocated. * @return pointer to the room for new data in @it->ptr if making room. * @return pointer to data right after the deleted fragment in @it->ptr. */ static int __split_linear_data(struct sk_buff *skb, char *pspt, int len, TfwStr *it) { int alloc = len > 0; int tail_len = (char *)skb_tail_pointer(skb) - pspt; struct page *page = virt_to_head_page(skb->head); SS_DBG("[%d]: %s: skb [%p] pspt [%p] len [%d] tail_len [%d]\n", smp_processor_id(), __func__, skb, pspt, len, tail_len); BUG_ON(!skb->head_frag); BUG_ON(tail_len <= 0); BUG_ON(!(alloc | tail_len)); BUG_ON(-len > tail_len); /* * Quick and unlikely path: just advance the skb tail pointer. * Note that this only works when we make room. When we remove, * pspt points at the start of the data chunk to remove. In that * case, tail_len can never be zero. */ if (unlikely(!tail_len && len <= ss_skb_tailroom(skb))) { BUG_ON(len < 0); it->ptr = ss_skb_put(skb, len); return 0; } /* * Quick and unlikely path: just move skb tail pointer backward. * Note that this only works when we remove data, and the data * is located exactly at the end of the linear part of an skb. */ if (unlikely((len < 0) && (tail_len == -len))) { ss_skb_put(skb, len); if (skb_is_nonlinear(skb)) it->ptr = skb_frag_address(&skb_shinfo(skb)->frags[0]); return 0; } /* * Data is inserted or deleted in the middle of the linear part, * or there's insufficient room in the linear part of an SKB to * insert @len bytes. * * Don't bother with skb tail room: if the linear part is large, * then it's likely that we'll do some smaller data insertions * later and go by the quick path above. Otherwise, the tail size * is also small. * * The inserted data is placed in a fragment. The tail part is * moved to yet another fragment. The linear part is trimmed to * exclude the deleted data and the tail part. * * Do all allocations before moving the fragments to avoid complex * rollback. */ if (alloc) { if (__new_pgfrag(skb, len, 0, alloc + !!tail_len, it)) return -EFAULT; } else { if (__extend_pgfrags(skb, 0, 1, it)) return -EFAULT; tail_len += len; /* @len is negative. */ } if (tail_len) { int tail_off = pspt - (char *)page_address(page); /* * Trim the linear part by |@len| bytes if data * is deleted. Then trim it further to exclude * the tail data. Finally, set up the fragment * allotted above with the tail data. */ if (len < 0) { tail_off -= len; skb->tail += len; skb->len += len; } skb->tail -= tail_len; skb->data_len += tail_len; skb->truesize += tail_len; __skb_fill_page_desc(skb, alloc, page, tail_off, tail_len); skb_frag_ref(skb, alloc); /* get_page(page); */ } it->ptr = skb_frag_address(&skb_shinfo(skb)->frags[0]); return 0; }
static int ipcomp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb) { int err = 0; u8 nexthdr = 0; u8 *prevhdr; int hdr_len = skb->h.raw - skb->nh.raw; unsigned char *tmp_hdr = NULL; struct ipv6hdr *iph; int plen, dlen; struct ipcomp_data *ipcd = x->data; u8 *start, *scratch = ipcd->scratch; if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && skb_linearize(skb, GFP_ATOMIC) != 0) { err = -ENOMEM; goto out; } skb->ip_summed = CHECKSUM_NONE; /* Remove ipcomp header and decompress original payload */ iph = skb->nh.ipv6h; tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC); if (!tmp_hdr) goto out; memcpy(tmp_hdr, iph, hdr_len); nexthdr = *(u8 *)skb->data; skb_pull(skb, sizeof(struct ipv6_comp_hdr)); skb->nh.raw += sizeof(struct ipv6_comp_hdr); memcpy(skb->nh.raw, tmp_hdr, hdr_len); iph = skb->nh.ipv6h; iph->payload_len = htons(ntohs(iph->payload_len) - sizeof(struct ipv6_comp_hdr)); skb->h.raw = skb->data; /* decompression */ plen = skb->len; dlen = IPCOMP_SCRATCH_SIZE; start = skb->data; err = crypto_comp_decompress(ipcd->tfm, start, plen, scratch, &dlen); if (err) { err = -EINVAL; goto out; } if (dlen < (plen + sizeof(struct ipv6_comp_hdr))) { err = -EINVAL; goto out; } err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC); if (err) { goto out; } skb_put(skb, dlen - plen); memcpy(skb->data, scratch, dlen); iph = skb->nh.ipv6h; iph->payload_len = htons(skb->len); ip6_find_1stfragopt(skb, &prevhdr); *prevhdr = nexthdr; out: if (tmp_hdr) kfree(tmp_hdr); if (err) goto error_out; return nexthdr; error_out: return err; }
static int ipcomp6_output(struct sk_buff **pskb) { int err; struct dst_entry *dst = (*pskb)->dst; struct xfrm_state *x = dst->xfrm; struct ipv6hdr *tmp_iph = NULL, *iph, *top_iph; int hdr_len = 0; struct ipv6_comp_hdr *ipch; struct ipcomp_data *ipcd = x->data; u8 *prevhdr; u8 nexthdr = 0; int plen, dlen; u8 *start, *scratch = ipcd->scratch; if ((*pskb)->ip_summed == CHECKSUM_HW) { err = skb_checksum_help(pskb, 0); if (err) goto error_nolock; } spin_lock_bh(&x->lock); err = xfrm_check_output(x, *pskb, AF_INET6); if (err) goto error; if (x->props.mode) { hdr_len = sizeof(struct ipv6hdr); nexthdr = IPPROTO_IPV6; iph = (*pskb)->nh.ipv6h; top_iph = (struct ipv6hdr *)skb_push(*pskb, sizeof(struct ipv6hdr)); top_iph->version = 6; top_iph->priority = iph->priority; top_iph->flow_lbl[0] = iph->flow_lbl[0]; top_iph->flow_lbl[1] = iph->flow_lbl[1]; top_iph->flow_lbl[2] = iph->flow_lbl[2]; top_iph->nexthdr = IPPROTO_IPV6; /* initial */ top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr)); top_iph->hop_limit = iph->hop_limit; memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr)); memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr)); (*pskb)->nh.raw = (*pskb)->data; /* == top_iph */ (*pskb)->h.raw = (*pskb)->nh.raw + hdr_len; } else { hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr); nexthdr = *prevhdr; } /* check whether datagram len is larger than threshold */ if (((*pskb)->len - hdr_len) < ipcd->threshold) { goto out_ok; } if ((skb_is_nonlinear(*pskb) || skb_cloned(*pskb)) && skb_linearize(*pskb, GFP_ATOMIC) != 0) { err = -ENOMEM; goto error; } /* compression */ plen = (*pskb)->len - hdr_len; dlen = IPCOMP_SCRATCH_SIZE; start = (*pskb)->data + hdr_len; err = crypto_comp_compress(ipcd->tfm, start, plen, scratch, &dlen); if (err) { goto error; } if ((dlen + sizeof(struct ipv6_comp_hdr)) >= plen) { goto out_ok; } memcpy(start, scratch, dlen); pskb_trim(*pskb, hdr_len+dlen); /* insert ipcomp header and replace datagram */ tmp_iph = kmalloc(hdr_len, GFP_ATOMIC); if (!tmp_iph) { err = -ENOMEM; goto error; } memcpy(tmp_iph, (*pskb)->nh.raw, hdr_len); top_iph = (struct ipv6hdr*)skb_push(*pskb, sizeof(struct ipv6_comp_hdr)); memcpy(top_iph, tmp_iph, hdr_len); kfree(tmp_iph); if (x->props.mode && (x->props.flags & XFRM_STATE_NOECN)) IP6_ECN_clear(top_iph); top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr)); (*pskb)->nh.raw = (*pskb)->data; /* top_iph */ ip6_find_1stfragopt(*pskb, &prevhdr); *prevhdr = IPPROTO_COMP; ipch = (struct ipv6_comp_hdr *)((unsigned char *)top_iph + hdr_len); ipch->nexthdr = nexthdr; ipch->flags = 0; ipch->cpi = htons((u16 )ntohl(x->id.spi)); (*pskb)->h.raw = (unsigned char*)ipch; out_ok: x->curlft.bytes += (*pskb)->len; x->curlft.packets++; spin_unlock_bh(&x->lock); if (((*pskb)->dst = dst_pop(dst)) == NULL) { err = -EHOSTUNREACH; goto error_nolock; } err = NET_XMIT_BYPASS; out_exit: return err; error: spin_unlock_bh(&x->lock); error_nolock: kfree_skb(*pskb); goto out_exit; }
/** * sk_run_filter - run a filter on a socket * @skb: buffer to run the filter on * @filter: filter to apply * * Decode and apply filter instructions to the skb->data. * Return length to keep, 0 for none. @skb is the data we are * filtering, @filter is the array of filter instructions. * Because all jumps are guaranteed to be before last instruction, * and last instruction guaranteed to be a RET, we dont need to check * flen. (We used to pass to this function the length of filter) */ unsigned int sk_run_filter(const struct sk_buff *skb, const struct sock_filter *fentry) { void *ptr; u32 A = 0; /* Accumulator */ u32 X = 0; /* Index Register */ u32 mem[BPF_MEMWORDS]; /* Scratch Memory Store */ unsigned long memvalid = 0; u32 tmp; int k; BUILD_BUG_ON(BPF_MEMWORDS > BITS_PER_LONG); /* * Process array of filter instructions. */ for (;; fentry++) { #if defined(CONFIG_X86_32) #define K (fentry->k) #else const u32 K = fentry->k; #endif switch (fentry->code) { case BPF_S_ALU_ADD_X: A += X; continue; case BPF_S_ALU_ADD_K: A += K; continue; case BPF_S_ALU_SUB_X: A -= X; continue; case BPF_S_ALU_SUB_K: A -= K; continue; case BPF_S_ALU_MUL_X: A *= X; continue; case BPF_S_ALU_MUL_K: A *= K; continue; case BPF_S_ALU_DIV_X: if (X == 0) return 0; A /= X; continue; case BPF_S_ALU_DIV_K: A /= K; continue; case BPF_S_ALU_AND_X: A &= X; continue; case BPF_S_ALU_AND_K: A &= K; continue; case BPF_S_ALU_OR_X: A |= X; continue; case BPF_S_ALU_OR_K: A |= K; continue; case BPF_S_ALU_LSH_X: A <<= X; continue; case BPF_S_ALU_LSH_K: A <<= K; continue; case BPF_S_ALU_RSH_X: A >>= X; continue; case BPF_S_ALU_RSH_K: A >>= K; continue; case BPF_S_ALU_NEG: A = -A; continue; case BPF_S_JMP_JA: fentry += K; continue; case BPF_S_JMP_JGT_K: fentry += (A > K) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JGE_K: fentry += (A >= K) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JEQ_K: fentry += (A == K) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JSET_K: fentry += (A & K) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JGT_X: fentry += (A > X) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JGE_X: fentry += (A >= X) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JEQ_X: fentry += (A == X) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JSET_X: fentry += (A & X) ? fentry->jt : fentry->jf; continue; case BPF_S_LD_W_ABS: k = K; load_w: ptr = load_pointer(skb, k, 4, &tmp); if (ptr != NULL) { A = get_unaligned_be32(ptr); continue; } break; case BPF_S_LD_H_ABS: k = K; load_h: ptr = load_pointer(skb, k, 2, &tmp); if (ptr != NULL) { A = get_unaligned_be16(ptr); continue; } break; case BPF_S_LD_B_ABS: k = K; load_b: ptr = load_pointer(skb, k, 1, &tmp); if (ptr != NULL) { A = *(u8 *)ptr; continue; } break; case BPF_S_LD_W_LEN: A = skb->len; continue; case BPF_S_LDX_W_LEN: X = skb->len; continue; case BPF_S_LD_W_IND: k = X + K; goto load_w; case BPF_S_LD_H_IND: k = X + K; goto load_h; case BPF_S_LD_B_IND: k = X + K; goto load_b; case BPF_S_LDX_B_MSH: ptr = load_pointer(skb, K, 1, &tmp); if (ptr != NULL) { X = (*(u8 *)ptr & 0xf) << 2; continue; } return 0; case BPF_S_LD_IMM: A = K; continue; case BPF_S_LDX_IMM: X = K; continue; case BPF_S_LD_MEM: A = (memvalid & (1UL << K)) ? mem[K] : 0; continue; case BPF_S_LDX_MEM: X = (memvalid & (1UL << K)) ? mem[K] : 0; continue; case BPF_S_MISC_TAX: X = A; continue; case BPF_S_MISC_TXA: A = X; continue; case BPF_S_RET_K: return K; case BPF_S_RET_A: return A; case BPF_S_ST: memvalid |= 1UL << K; mem[K] = A; continue; case BPF_S_STX: memvalid |= 1UL << K; mem[K] = X; continue; default: WARN_RATELIMIT(1, "Unknown code:%u jt:%u tf:%u k:%u\n", fentry->code, fentry->jt, fentry->jf, fentry->k); return 0; } /* * Handle ancillary data, which are impossible * (or very difficult) to get parsing packet contents. */ switch (k-SKF_AD_OFF) { case SKF_AD_PROTOCOL: A = ntohs(skb->protocol); continue; case SKF_AD_PKTTYPE: A = skb->pkt_type; continue; case SKF_AD_IFINDEX: if (!skb->dev) return 0; A = skb->dev->ifindex; continue; case SKF_AD_MARK: A = skb->mark; continue; case SKF_AD_QUEUE: A = skb->queue_mapping; continue; case SKF_AD_HATYPE: if (!skb->dev) return 0; A = skb->dev->type; continue; #if 0 case SKF_AD_RXHASH: A = skb->rxhash; continue; #endif case SKF_AD_CPU: A = raw_smp_processor_id(); continue; case SKF_AD_NLATTR: { struct nlattr *nla; if (skb_is_nonlinear(skb)) return 0; if (A > skb->len - sizeof(struct nlattr)) return 0; nla = nla_find((struct nlattr *)&skb->data[A], skb->len - A, X); if (nla) A = (void *)nla - (void *)skb->data; else A = 0; continue; } case SKF_AD_NLATTR_NEST: { struct nlattr *nla; if (skb_is_nonlinear(skb)) return 0; if (A > skb->len - sizeof(struct nlattr)) return 0; nla = (struct nlattr *)&skb->data[A]; if (nla->nla_len > A - skb->len) return 0; nla = nla_find_nested(nla, X); if (nla) A = (void *)nla - (void *)skb->data; else A = 0; continue; } default: return 0; } } return 0; }
static int xfrm4_output_one(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct xfrm_state *x = dst->xfrm; int err; /* purpose: 0014838 author: paul.chen date: 2011-12-06 */ /* description: Fix reboot & plug in crash for VPN G2G wildcard */ if(skb && skb->nh.iph && skb->nh.iph->protocol==IPPROTO_IGMP) { err = -EINVAL; goto error_nolock; } if (skb->ip_summed == CHECKSUM_HW) { err = skb_checksum_help(skb, 0); if (err) goto error_nolock; } if (x->props.mode) { err = xfrm4_tunnel_check_size(skb); if (err) goto error_nolock; } do { spin_lock_bh(&x->lock); err = xfrm_state_check(x, skb); if (err) goto error; #if defined(CONFIG_CAVIUM_OCTEON_IPSEC) && defined(CONFIG_NET_KEY) /* * If Octeon IPSEC Acceleration module has been loaded * call it, otherwise, follow the software path */ if(cavium_ipsec_process) { if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) { err = -ENOMEM; goto error; } err = cavium_ipsec_process(x->sa_handle, skb, 0, 1 /*ENCRYPT*/); } else { xfrm4_encap(skb); err = x->type->output(x, skb); } #else xfrm4_encap(skb); err = x->type->output(x, skb); #endif if (err) goto error; x->curlft.bytes += skb->len; x->curlft.packets++; spin_unlock_bh(&x->lock); if (!(skb->dst = dst_pop(dst))) { err = -EHOSTUNREACH; goto error_nolock; } dst = skb->dst; x = dst->xfrm; } while (x && !x->props.mode); IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; err = 0; out_exit: return err; error: spin_unlock_bh(&x->lock); error_nolock: kfree_skb(skb); goto out_exit; }
/** * sk_run_filter - run a filter on a socket * @skb: buffer to run the filter on * @fentry: filter to apply * * Decode and apply filter instructions to the skb->data. * Return length to keep, 0 for none. @skb is the data we are * filtering, @filter is the array of filter instructions. * Because all jumps are guaranteed to be before last instruction, * and last instruction guaranteed to be a RET, we dont need to check * flen. (We used to pass to this function the length of filter) */ unsigned int sk_run_filter(const struct sk_buff *skb, const struct sock_filter *fentry) { void *ptr; u32 A = 0; /* Accumulator */ u32 X = 0; /* Index Register */ u32 mem[BPF_MEMWORDS]; /* Scratch Memory Store */ u32 tmp; int k; /* * Process array of filter instructions. */ for (;; fentry++) { #if defined(CONFIG_X86_32) #define K (fentry->k) #else const u32 K = fentry->k; #endif switch (fentry->code) { case BPF_S_ALU_ADD_X: A += X; continue; case BPF_S_ALU_ADD_K: A += K; continue; case BPF_S_ALU_SUB_X: A -= X; continue; case BPF_S_ALU_SUB_K: A -= K; continue; case BPF_S_ALU_MUL_X: A *= X; continue; case BPF_S_ALU_MUL_K: A *= K; continue; case BPF_S_ALU_DIV_X: if (X == 0) return 0; A /= X; continue; case BPF_S_ALU_DIV_K: A = reciprocal_divide(A, K); continue; case BPF_S_ALU_AND_X: A &= X; continue; case BPF_S_ALU_AND_K: A &= K; continue; case BPF_S_ALU_OR_X: A |= X; continue; case BPF_S_ALU_OR_K: A |= K; continue; case BPF_S_ALU_LSH_X: A <<= X; continue; case BPF_S_ALU_LSH_K: A <<= K; continue; case BPF_S_ALU_RSH_X: A >>= X; continue; case BPF_S_ALU_RSH_K: A >>= K; continue; case BPF_S_ALU_NEG: A = -A; continue; case BPF_S_JMP_JA: fentry += K; continue; case BPF_S_JMP_JGT_K: fentry += (A > K) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JGE_K: fentry += (A >= K) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JEQ_K: fentry += (A == K) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JSET_K: fentry += (A & K) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JGT_X: fentry += (A > X) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JGE_X: fentry += (A >= X) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JEQ_X: fentry += (A == X) ? fentry->jt : fentry->jf; continue; case BPF_S_JMP_JSET_X: fentry += (A & X) ? fentry->jt : fentry->jf; continue; case BPF_S_LD_W_ABS: k = K; load_w: ptr = load_pointer(skb, k, 4, &tmp); if (ptr != NULL) { A = get_unaligned_be32(ptr); continue; } return 0; case BPF_S_LD_H_ABS: k = K; load_h: ptr = load_pointer(skb, k, 2, &tmp); if (ptr != NULL) { A = get_unaligned_be16(ptr); continue; } return 0; case BPF_S_LD_B_ABS: k = K; load_b: ptr = load_pointer(skb, k, 1, &tmp); if (ptr != NULL) { A = *(u8 *)ptr; continue; } return 0; case BPF_S_LD_W_LEN: A = skb->len; continue; case BPF_S_LDX_W_LEN: X = skb->len; continue; case BPF_S_LD_W_IND: k = X + K; goto load_w; case BPF_S_LD_H_IND: k = X + K; goto load_h; case BPF_S_LD_B_IND: k = X + K; goto load_b; case BPF_S_LDX_B_MSH: ptr = load_pointer(skb, K, 1, &tmp); if (ptr != NULL) { X = (*(u8 *)ptr & 0xf) << 2; continue; } return 0; case BPF_S_LD_IMM: A = K; continue; case BPF_S_LDX_IMM: X = K; continue; case BPF_S_LD_MEM: A = mem[K]; continue; case BPF_S_LDX_MEM: X = mem[K]; continue; case BPF_S_MISC_TAX: X = A; continue; case BPF_S_MISC_TXA: A = X; continue; case BPF_S_RET_K: return K; case BPF_S_RET_A: return A; case BPF_S_ST: mem[K] = A; continue; case BPF_S_STX: mem[K] = X; continue; case BPF_S_ANC_PROTOCOL: A = ntohs(skb->protocol); continue; case BPF_S_ANC_PKTTYPE: A = skb->pkt_type; continue; case BPF_S_ANC_IFINDEX: if (!skb->dev) return 0; A = skb->dev->ifindex; continue; case BPF_S_ANC_MARK: A = skb->mark; continue; case BPF_S_ANC_QUEUE: A = skb->queue_mapping; continue; case BPF_S_ANC_HATYPE: if (!skb->dev) return 0; A = skb->dev->type; continue; case BPF_S_ANC_RXHASH: A = skb->rxhash; continue; case BPF_S_ANC_CPU: A = raw_smp_processor_id(); continue; case BPF_S_ANC_NLATTR: { struct nlattr *nla; if (skb_is_nonlinear(skb)) return 0; if (A > skb->len - sizeof(struct nlattr)) return 0; nla = nla_find((struct nlattr *)&skb->data[A], skb->len - A, X); if (nla) A = (void *)nla - (void *)skb->data; else A = 0; continue; } case BPF_S_ANC_NLATTR_NEST: { struct nlattr *nla; if (skb_is_nonlinear(skb)) return 0; if (A > skb->len - sizeof(struct nlattr)) return 0; nla = (struct nlattr *)&skb->data[A]; if (nla->nla_len > A - skb->len) return 0; nla = nla_find_nested(nla, X); if (nla) A = (void *)nla - (void *)skb->data; else A = 0; continue; } #ifdef CONFIG_SECCOMP_FILTER case BPF_S_ANC_SECCOMP_LD_W: A = seccomp_bpf_load(fentry->k); continue; #endif default: WARN_RATELIMIT(1, "Unknown code:%u jt:%u tf:%u k:%u\n", fentry->code, fentry->jt, fentry->jf, fentry->k); return 0; } } return 0; }
static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) const void *hdr, u_int16_t datalen, #endif int *hotdrop) { const struct ipt_p2p_info *info = matchinfo; unsigned char *haystack; struct iphdr *ip = skb->nh.iph; int p2p_result = 0, i = 0; // int head_len; int hlen = ntohs(ip->tot_len)-(ip->ihl*4); /*hlen = packet-data length*/ /*must not be a fragment*/ if (offset) { if (info->debug) printk("IPP2P.match: offset found %i \n",offset); return 0; } /*make sure that skb is linear*/ if(skb_is_nonlinear(skb)) { if (info->debug) printk("IPP2P.match: nonlinear skb found\n"); return 0; } haystack=(char *)ip+(ip->ihl*4); /*haystack = packet data*/ switch (ip->protocol) { case IPPROTO_TCP: /*what to do with a TCP packet*/ { struct tcphdr *tcph = (void *) ip + ip->ihl * 4; if (tcph->fin) return 0; /*if FIN bit is set bail out*/ if (tcph->syn) return 0; /*if SYN bit is set bail out*/ if (tcph->rst) return 0; /*if RST bit is set bail out*/ haystack += tcph->doff * 4; /*get TCP-Header-Size*/ hlen -= tcph->doff * 4; while (matchlist[i].command) { if ((((info->cmd & matchlist[i].command) == matchlist[i].command) || ((info->cmd & matchlist[i].short_hand) == matchlist[i].short_hand)) && (hlen > matchlist[i].packet_len)) { p2p_result = matchlist[i].function_name(haystack, hlen); if (p2p_result) { if (info->debug) printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen); return p2p_result; } } i++; } return p2p_result; } case IPPROTO_UDP: /*what to do with an UDP packet*/ { struct udphdr *udph = (void *) ip + ip->ihl * 4; while (udp_list[i].command) { if ((((info->cmd & udp_list[i].command) == udp_list[i].command) || ((info->cmd & udp_list[i].short_hand) == udp_list[i].short_hand)) && (hlen > udp_list[i].packet_len)) { p2p_result = udp_list[i].function_name(haystack, hlen); if (p2p_result) { if (info->debug) printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", p2p_result, NIPQUAD(ip->saddr),ntohs(udph->source), NIPQUAD(ip->daddr),ntohs(udph->dest),hlen); return p2p_result; } } i++; } return p2p_result; } default: return 0; } }
static bool match(const struct sk_buff *skb, struct xt_action_param *par) { const struct ipt_weburl_info *info = (const struct ipt_weburl_info*)(par->matchinfo); int test = 0; struct iphdr* iph; /* linearize skb if necessary */ struct sk_buff *linear_skb; int skb_copied; if(skb_is_nonlinear(skb)) { linear_skb = skb_copy(skb, GFP_ATOMIC); skb_copied = 1; } else { linear_skb = (struct sk_buff*)skb; skb_copied = 0; } /* ignore packets that are not TCP */ iph = (struct iphdr*)(skb_network_header(skb)); if(iph->protocol == IPPROTO_TCP) { /* get payload */ struct tcphdr* tcp_hdr = (struct tcphdr*)( ((unsigned char*)iph) + (iph->ihl*4) ); unsigned short payload_offset = (tcp_hdr->doff*4) + (iph->ihl*4); unsigned char* payload = ((unsigned char*)iph) + payload_offset; unsigned short payload_length = ntohs(iph->tot_len) - payload_offset; /* if payload length <= 10 bytes don't bother doing a check, otherwise check for match */ if(payload_length > 10) { if(strnicmp((char*)payload, "GET ", 4) == 0 || strnicmp( (char*)payload, "POST ", 5) == 0 || strnicmp((char*)payload, "HEAD ", 5) == 0) { test = http_match(info, payload, payload_length); } else if ((unsigned short)ntohs(tcp_hdr->dest) == 443) { test = https_match(info, payload, payload_length); } } } /* free skb if we made a copy to linearize it */ if(skb_copied == 1) { kfree_skb(linear_skb); } /* printk("returning %d from weburl\n\n\n", test); */ return test; }
/** * sk_run_filter - run a filter on a socket * @skb: buffer to run the filter on * @filter: filter to apply * @flen: length of filter * * Decode and apply filter instructions to the skb->data. * Return length to keep, 0 for none. skb is the data we are * filtering, filter is the array of filter instructions, and * len is the number of filter blocks in the array. */ unsigned int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen) { void *ptr; u32 A = 0; /* Accumulator */ u32 X = 0; /* Index Register */ u32 mem[BPF_MEMWORDS]; /* Scratch Memory Store */ unsigned long memvalid = 0; u32 tmp; int k; int pc; BUILD_BUG_ON(BPF_MEMWORDS > BITS_PER_LONG); /* * Process array of filter instructions. */ for (pc = 0; pc < flen; pc++) { const struct sock_filter *fentry = &filter[pc]; u32 f_k = fentry->k; switch (fentry->code) { case BPF_ALU|BPF_ADD|BPF_X: A += X; continue; case BPF_ALU|BPF_ADD|BPF_K: A += f_k; continue; case BPF_ALU|BPF_SUB|BPF_X: A -= X; continue; case BPF_ALU|BPF_SUB|BPF_K: A -= f_k; continue; case BPF_ALU|BPF_MUL|BPF_X: A *= X; continue; case BPF_ALU|BPF_MUL|BPF_K: A *= f_k; continue; case BPF_ALU|BPF_DIV|BPF_X: if (X == 0) return 0; A /= X; continue; case BPF_ALU|BPF_DIV|BPF_K: A /= f_k; continue; case BPF_ALU|BPF_AND|BPF_X: A &= X; continue; case BPF_ALU|BPF_AND|BPF_K: A &= f_k; continue; case BPF_ALU|BPF_OR|BPF_X: A |= X; continue; case BPF_ALU|BPF_OR|BPF_K: A |= f_k; continue; case BPF_ALU|BPF_LSH|BPF_X: A <<= X; continue; case BPF_ALU|BPF_LSH|BPF_K: A <<= f_k; continue; case BPF_ALU|BPF_RSH|BPF_X: A >>= X; continue; case BPF_ALU|BPF_RSH|BPF_K: A >>= f_k; continue; case BPF_ALU|BPF_NEG: A = -A; continue; case BPF_JMP|BPF_JA: pc += f_k; continue; case BPF_JMP|BPF_JGT|BPF_K: pc += (A > f_k) ? fentry->jt : fentry->jf; continue; case BPF_JMP|BPF_JGE|BPF_K: pc += (A >= f_k) ? fentry->jt : fentry->jf; continue; case BPF_JMP|BPF_JEQ|BPF_K: pc += (A == f_k) ? fentry->jt : fentry->jf; continue; case BPF_JMP|BPF_JSET|BPF_K: pc += (A & f_k) ? fentry->jt : fentry->jf; continue; case BPF_JMP|BPF_JGT|BPF_X: pc += (A > X) ? fentry->jt : fentry->jf; continue; case BPF_JMP|BPF_JGE|BPF_X: pc += (A >= X) ? fentry->jt : fentry->jf; continue; case BPF_JMP|BPF_JEQ|BPF_X: pc += (A == X) ? fentry->jt : fentry->jf; continue; case BPF_JMP|BPF_JSET|BPF_X: pc += (A & X) ? fentry->jt : fentry->jf; continue; case BPF_LD|BPF_W|BPF_ABS: k = f_k; load_w: ptr = load_pointer(skb, k, 4, &tmp); if (ptr != NULL) { A = get_unaligned_be32(ptr); continue; } break; case BPF_LD|BPF_H|BPF_ABS: k = f_k; load_h: ptr = load_pointer(skb, k, 2, &tmp); if (ptr != NULL) { A = get_unaligned_be16(ptr); continue; } break; case BPF_LD|BPF_B|BPF_ABS: k = f_k; load_b: ptr = load_pointer(skb, k, 1, &tmp); if (ptr != NULL) { A = *(u8 *)ptr; continue; } break; case BPF_LD|BPF_W|BPF_LEN: A = skb->len; continue; case BPF_LDX|BPF_W|BPF_LEN: X = skb->len; continue; case BPF_LD|BPF_W|BPF_IND: k = X + f_k; goto load_w; case BPF_LD|BPF_H|BPF_IND: k = X + f_k; goto load_h; case BPF_LD|BPF_B|BPF_IND: k = X + f_k; goto load_b; case BPF_LDX|BPF_B|BPF_MSH: ptr = load_pointer(skb, f_k, 1, &tmp); if (ptr != NULL) { X = (*(u8 *)ptr & 0xf) << 2; continue; } return 0; case BPF_LD|BPF_IMM: A = f_k; continue; case BPF_LDX|BPF_IMM: X = f_k; continue; case BPF_LD|BPF_MEM: A = (memvalid & (1UL << f_k)) ? mem[f_k] : 0; continue; case BPF_LDX|BPF_MEM: X = (memvalid & (1UL << f_k)) ? mem[f_k] : 0; continue; case BPF_MISC|BPF_TAX: X = A; continue; case BPF_MISC|BPF_TXA: A = X; continue; case BPF_RET|BPF_K: return f_k; case BPF_RET|BPF_A: return A; case BPF_ST: memvalid |= 1UL << f_k; mem[f_k] = A; continue; case BPF_STX: memvalid |= 1UL << f_k; mem[f_k] = X; continue; default: WARN_ON(1); return 0; } /* * Handle ancillary data, which are impossible * (or very difficult) to get parsing packet contents. */ switch (k-SKF_AD_OFF) { case SKF_AD_PROTOCOL: A = ntohs(skb->protocol); continue; case SKF_AD_PKTTYPE: A = skb->pkt_type; continue; case SKF_AD_IFINDEX: A = skb->dev->ifindex; continue; case SKF_AD_NLATTR: { struct nlattr *nla; if (skb_is_nonlinear(skb)) return 0; if (A > skb->len - sizeof(struct nlattr)) return 0; nla = nla_find((struct nlattr *)&skb->data[A], skb->len - A, X); if (nla) A = (void *)nla - (void *)skb->data; else A = 0; continue; } case SKF_AD_NLATTR_NEST: { struct nlattr *nla; if (skb_is_nonlinear(skb)) return 0; if (A > skb->len - sizeof(struct nlattr)) return 0; nla = (struct nlattr *)&skb->data[A]; if (nla->nla_len > A - skb->len) return 0; nla = nla_find_nested(nla, X); if (nla) A = (void *)nla - (void *)skb->data; else A = 0; continue; } default: return 0; } } return 0; }
/* * Handle ICMP messages in the outside-to-inside direction (incoming) * and sometimes in outgoing direction from ip_vs_forward_icmp. * Find any that might be relevant, check against existing connections, * forward to the right destination host if relevant. * Currently handles error types - unreachable, quench, ttl exceeded. */ static int ip_vs_in_icmp(struct sk_buff **skb_p) { struct sk_buff *skb = *skb_p; struct iphdr *iph; struct icmphdr *icmph; struct iphdr *ciph; /* The ip header contained within the ICMP */ __u16 *pptr; /* port numbers from TCP/UDP contained header */ unsigned short len; unsigned short clen, csize; struct ip_vs_conn *cp; struct rtable *rt; /* Route to the other host */ int mtu; if (skb_is_nonlinear(skb)) { if (skb_linearize(skb, GFP_ATOMIC) != 0) return NF_DROP; } iph = skb->nh.iph; ip_send_check(iph); icmph = (struct icmphdr *)((char *)iph + (iph->ihl << 2)); len = ntohs(iph->tot_len) - (iph->ihl<<2); if (len < sizeof(struct icmphdr)) return NF_DROP; IP_VS_DBG(12, "icmp in (%d,%d) %u.%u.%u.%u -> %u.%u.%u.%u\n", icmph->type, ntohs(icmp_id(icmph)), NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); if ((icmph->type != ICMP_DEST_UNREACH) && (icmph->type != ICMP_SOURCE_QUENCH) && (icmph->type != ICMP_TIME_EXCEEDED)) return NF_ACCEPT; /* * If we get here we have an ICMP error of one of the above 3 types * Now find the contained IP header */ clen = len - sizeof(struct icmphdr); if (clen < sizeof(struct iphdr)) return NF_DROP; ciph = (struct iphdr *) (icmph + 1); csize = ciph->ihl << 2; if (clen < csize) return NF_DROP; /* We are only interested ICMPs generated from TCP or UDP packets */ if (ciph->protocol != IPPROTO_UDP && ciph->protocol != IPPROTO_TCP) return NF_ACCEPT; /* Skip non-first embedded TCP/UDP fragments */ if (ciph->frag_off & __constant_htons(IP_OFFSET)) return NF_ACCEPT; /* We need at least TCP/UDP ports here */ if (clen < csize + sizeof(struct udphdr)) return NF_DROP; /* Ensure the checksum is correct */ if (ip_compute_csum((unsigned char *) icmph, len)) { /* Failed checksum! */ IP_VS_ERR_RL("incoming ICMP: failed checksum from " "%d.%d.%d.%d!\n", NIPQUAD(iph->saddr)); return NF_DROP; } pptr = (__u16 *)&(((char *)ciph)[csize]); IP_VS_DBG(11, "Handling incoming ICMP for " "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n", NIPQUAD(ciph->saddr), ntohs(pptr[0]), NIPQUAD(ciph->daddr), ntohs(pptr[1])); /* This is pretty much what ip_vs_conn_in_get() does, except parameters are in the reverse order */ cp = ip_vs_conn_in_get(ciph->protocol, ciph->daddr, pptr[1], ciph->saddr, pptr[0]); if (cp == NULL) return NF_ACCEPT; ip_vs_in_stats(cp, skb); /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be forwarded directly here, because there is no need to translate address/port back */ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { int ret; if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp); else ret = NF_ACCEPT; atomic_inc(&cp->in_pkts); ip_vs_conn_put(cp); return ret; } /* * mangle and send the packet here */ if (!(rt = __ip_vs_get_out_rt(cp, RT_TOS(iph->tos)))) goto tx_error_icmp; /* MTU checking */ mtu = rt->u.dst.pmtu; if ((skb->len > mtu) && (iph->frag_off&__constant_htons(IP_DF))) { ip_rt_put(rt); icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n"); goto tx_error; } /* drop old route */ dst_release(skb->dst); skb->dst = &rt->u.dst; /* copy-on-write the packet before mangling it */ if (ip_vs_skb_cow(skb, rt->u.dst.dev->hard_header_len, &iph, (unsigned char**)&icmph)) { ip_vs_conn_put(cp); return NF_DROP; } ciph = (struct iphdr *) (icmph + 1); pptr = (__u16 *)&(((char *)ciph)[csize]); /* The ICMP packet for VS/NAT must be written to correct addresses before being forwarded to the right server */ /* First change the dest IP address, and recalc checksum */ iph->daddr = cp->daddr; ip_send_check(iph); /* Now change the *source* address in the contained IP */ ciph->saddr = cp->daddr; ip_send_check(ciph); /* the TCP/UDP source port - cannot redo check */ pptr[0] = cp->dport; /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); skb->ip_summed = CHECKSUM_UNNECESSARY; IP_VS_DBG(11, "Forwarding incoming ICMP to " "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n", NIPQUAD(ciph->saddr), ntohs(pptr[0]), NIPQUAD(ciph->daddr), ntohs(pptr[1])); #ifdef CONFIG_NETFILTER_DEBUG skb->nf_debug = 1 << NF_IP_LOCAL_OUT; #endif /* CONFIG_NETFILTER_DEBUG */ ip_send(skb); ip_vs_conn_put(cp); return NF_STOLEN; tx_error_icmp: dst_link_failure(skb); tx_error: dev_kfree_skb(skb); ip_vs_conn_put(cp); return NF_STOLEN; }
/* * It is hooked at the NF_IP_FORWARD chain, used only for VS/NAT. * Check if outgoing packet belongs to the established ip_vs_conn, * rewrite addresses of the packet and send it on its way... */ static unsigned int ip_vs_out(unsigned int hooknum, struct sk_buff **skb_p, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *skb = *skb_p; struct iphdr *iph; union ip_vs_tphdr h; struct ip_vs_conn *cp; int size; int ihl; EnterFunction(11); if (skb->nfcache & NFC_IPVS_PROPERTY) return NF_ACCEPT; iph = skb->nh.iph; if (iph->protocol == IPPROTO_ICMP) return ip_vs_out_icmp(skb_p); /* let it go if other IP protocols */ if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP) return NF_ACCEPT; /* reassemble IP fragments */ if (iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { skb = ip_defrag(skb, IP_DEFRAG_VS_OUT); if (!skb) return NF_STOLEN; iph = skb->nh.iph; *skb_p = skb; } /* make sure that protocol header available in skb data area, note that skb data area may be reallocated. */ ihl = iph->ihl << 2; if (ip_vs_header_check(skb, iph->protocol, ihl) == -1) return NF_DROP; iph = skb->nh.iph; h.raw = (char*) iph + ihl; /* * Check if the packet belongs to an old entry */ cp = ip_vs_conn_out_get(iph->protocol, iph->saddr, h.portp[0], iph->daddr, h.portp[1]); if (!cp) { if (sysctl_ip_vs_nat_icmp_send && ip_vs_lookup_real_service(iph->protocol, iph->saddr, h.portp[0])) { /* * Notify the real server: there is no existing * entry if it is not RST packet or not TCP packet. */ if (!h.th->rst || iph->protocol != IPPROTO_TCP) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); kfree_skb(skb); return NF_STOLEN; } } IP_VS_DBG(12, "packet for %s %d.%d.%d.%d:%d " "continue traversal as normal.\n", ip_vs_proto_name(iph->protocol), NIPQUAD(iph->daddr), ntohs(h.portp[1])); if (skb_is_nonlinear(skb)) ip_send_check(iph); return NF_ACCEPT; } /* * If it has ip_vs_app helper, the helper may change the payload, * so it needs full checksum checking and checksum calculation. * If not, only the header (addr/port) is changed, so it is fast * to do incremental checksum update, and let the destination host * do final checksum checking. */ if (cp->app && skb_is_nonlinear(skb)) { if (skb_linearize(skb, GFP_ATOMIC) != 0) { ip_vs_conn_put(cp); return NF_DROP; } iph = skb->nh.iph; h.raw = (char*) iph + ihl; } size = skb->len - ihl; IP_VS_DBG(11, "O-pkt: %s size=%d\n", ip_vs_proto_name(iph->protocol), size); /* do TCP/UDP checksum checking if it has application helper */ if (cp->app && (iph->protocol != IPPROTO_UDP || h.uh->check != 0)) { switch (skb->ip_summed) { case CHECKSUM_NONE: skb->csum = csum_partial(h.raw, size, 0); case CHECKSUM_HW: if (csum_tcpudp_magic(iph->saddr, iph->daddr, size, iph->protocol, skb->csum)) { ip_vs_conn_put(cp); IP_VS_DBG_RL("Outgoing failed %s checksum " "from %d.%d.%d.%d (size=%d)!\n", ip_vs_proto_name(iph->protocol), NIPQUAD(iph->saddr), size); return NF_DROP; } break; default: /* CHECKSUM_UNNECESSARY */ break; } } IP_VS_DBG(11, "Outgoing %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d\n", ip_vs_proto_name(iph->protocol), NIPQUAD(iph->saddr), ntohs(h.portp[0]), NIPQUAD(iph->daddr), ntohs(h.portp[1])); /* mangle the packet */ iph->saddr = cp->vaddr; h.portp[0] = cp->vport; /* * Call application helper if needed */ if (ip_vs_app_pkt_out(cp, skb) != 0) { /* skb data has probably changed, update pointers */ iph = skb->nh.iph; h.raw = (char*)iph + ihl; size = skb->len - ihl; } /* * Adjust TCP/UDP checksums */ if (!cp->app && (iph->protocol != IPPROTO_UDP || h.uh->check != 0)) { /* Only port and addr are changed, do fast csum update */ ip_vs_fast_check_update(&h, cp->daddr, cp->vaddr, cp->dport, cp->vport, iph->protocol); if (skb->ip_summed == CHECKSUM_HW) skb->ip_summed = CHECKSUM_NONE; } else { /* full checksum calculation */ switch (iph->protocol) { case IPPROTO_TCP: h.th->check = 0; skb->csum = csum_partial(h.raw, size, 0); h.th->check = csum_tcpudp_magic(iph->saddr, iph->daddr, size, iph->protocol, skb->csum); IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n", ip_vs_proto_name(iph->protocol), h.th->check, (char*)&(h.th->check) - (char*)h.raw); break; case IPPROTO_UDP: h.uh->check = 0; skb->csum = csum_partial(h.raw, size, 0); h.uh->check = csum_tcpudp_magic(iph->saddr, iph->daddr, size, iph->protocol, skb->csum); if (h.uh->check == 0) h.uh->check = 0xFFFF; IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n", ip_vs_proto_name(iph->protocol), h.uh->check, (char*)&(h.uh->check) - (char*)h.raw); break; } } ip_send_check(iph); ip_vs_out_stats(cp, skb); ip_vs_set_state(cp, VS_STATE_OUTPUT, iph, h.portp); ip_vs_conn_put(cp); skb->nfcache |= NFC_IPVS_PROPERTY; LeaveFunction(11); return NF_ACCEPT; }
int ipsec6_input_check_esp(struct sk_buff **skb, struct ipv6_esp_hdr* esphdr, u8 *nexthdr) { int len = 0; int rtn = 0; struct sa_index sa_idx; u8 *authdata = NULL; u8 *srcdata = NULL; int srcsize = 0, totalsize = 0, hashsize = 0, encsize = 0; IPSEC6_DEBUG("start esp processing\n"); if (!(*skb&&esphdr)) { printk(KERN_ERR "ipsec6_input_check_esp: parameters are invalid\n"); goto finish; } if (skb_is_nonlinear(*skb)) { u16 offset = ((char*)esphdr) - (char*)((*skb)->nh.raw); if (!skb_linearize(*skb, GFP_ATOMIC)) { esphdr = (struct ipv6_esp_hdr*)((*skb)->nh.raw + offset); } else { printk(KERN_ERR "ipsec6_input_check_esp: counld not linearize skb\n"); rtn = -EINVAL; goto finish; } } /* Check SPI */ IPSEC6_DEBUG("esphdr->spi is 0x%x\n", ntohl(esphdr->spi)); sa_index_init(&sa_idx); ipv6_addr_copy(&((struct sockaddr_in6 *)&sa_idx.dst)->sin6_addr, &(*skb)->nh.ipv6h->daddr); ((struct sockaddr_in6 *)&sa_idx.dst)->sin6_family = AF_INET6; sa_idx.prefixlen_d = 128; sa_idx.ipsec_proto = SADB_SATYPE_ESP; sa_idx.spi = esphdr->spi; sa_idx.sa = sadb_find_by_sa_index(&sa_idx); if (!sa_idx.sa) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: not found SA for esp\n"); goto finish; } write_lock_bh(&sa_idx.sa->lock); IPSEC6_DEBUG("use kerneli version.\n"); if ( sa_idx.sa->esp_algo.algo == SADB_EALG_NONE ) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: not found encryption algorithm in SA!\n"); goto unlock_finish; } len = ntohs((*skb)->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); if (len > (*skb)->len + ((char*)(*skb)->data - (char*)(*skb)->nh.ipv6h)) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: received packet length is wrong\n"); goto unlock_finish; } totalsize = len - ((((char*)esphdr) - ((char*)(*skb)->nh.ipv6h))); if (!(sa_idx.sa->esp_algo.cx->ci)) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: not found esp algo\n"); goto unlock_finish; } if ( !check_replay_window(&sa_idx.sa->replay_window, esphdr->seq_no) ) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: replay check err!\n"); kfree(srcdata); goto unlock_finish; } encsize = totalsize - sa_idx.sa->esp_algo.cx->ci->ivsize - 8; /* 8 = SPI + Sequence Number */ if ( sa_idx.sa->auth_algo.algo != SADB_AALG_NONE ) { /* Calculate size */ /* The tail of payload does not have to be aligned */ /* with a multiple number of 64 bit. */ /* 64 bit alignment is adapted to the position of top of header.*/ hashsize = sa_idx.sa->auth_algo.digest_len; encsize -= hashsize; authdata=kmalloc(sa_idx.sa->auth_algo.dx->di->blocksize, GFP_ATOMIC); sa_idx.sa->auth_algo.dx->di->hmac_atomic(sa_idx.sa->auth_algo.dx, sa_idx.sa->auth_algo.key, sa_idx.sa->auth_algo.key_len, (char*)esphdr, totalsize - hashsize, authdata); /* Originally, IABG uses "for" loop for matching authentication data. */ /* I change it into memcmp routine. */ if (memcmp(authdata, &((char*)esphdr)[totalsize - hashsize], sa_idx.sa->auth_algo.digest_len )) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: invalid checksum in ESP\n"); kfree(authdata); goto unlock_finish; } kfree(authdata); authdata = NULL; } /* Decrypt data */ srcdata = kmalloc(encsize, GFP_ATOMIC); if (!srcdata) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: can't allocate memory for decrypt\n"); goto unlock_finish; } IPSEC6_DEBUG("len=%d, totalsize=%d, encsize=%d\n", len, totalsize, encsize); if (!(sa_idx.sa->esp_algo.iv)) { /* first packet */ sa_idx.sa->esp_algo.iv = kmalloc(sa_idx.sa->esp_algo.cx->ci->ivsize, GFP_ATOMIC); } memcpy(sa_idx.sa->esp_algo.iv, esphdr->enc_data, sa_idx.sa->esp_algo.cx->ci->ivsize); sa_idx.sa->esp_algo.cx->ci->decrypt_atomic_iv(sa_idx.sa->esp_algo.cx, ((u8 *)(esphdr->enc_data)) + sa_idx.sa->esp_algo.cx->ci->ivsize, srcdata, encsize, sa_idx.sa->esp_algo.iv); /* encsize - (pad_len + next_hdr) - pad_len */ srcsize = encsize - 2 - srcdata[encsize-2]; IPSEC6_DEBUG("Original data is srcsize=%d, padlength=%d\n", srcsize, srcdata[encsize-2]); if (srcsize <= 0) { if (net_ratelimit()) printk(KERN_ERR "ipsec6_input_check_esp: Encrypted packet contains garbage(Size of decrypted packet < 0).\n"); kfree(srcdata); goto unlock_finish; } update_replay_window(&sa_idx.sa->replay_window, esphdr->seq_no); *nexthdr = srcdata[encsize-1]; IPSEC6_DEBUG("nexthdr = %u\n", *nexthdr); memcpy(esphdr, srcdata, srcsize); skb_trim(*skb, (*skb)->len + srcsize - totalsize); (*skb)->nh.ipv6h->payload_len = htons(((char *)esphdr - (char *)((*skb)->nh.ipv6h)) - sizeof(struct ipv6hdr) + srcsize); /* ok ? -mk */ kfree(srcdata); srcdata = NULL; rtn = sa_idx.spi; /* Otherwise checksum of fragmented udp packets fails (udp.c, csum_fold) */ (*skb)->ip_summed = CHECKSUM_UNNECESSARY; (*skb)->security |= RCV_CRYPT; if (!sa_idx.sa->fuse_time) { sa_idx.sa->fuse_time = jiffies; sa_idx.sa->lifetime_c.usetime = (sa_idx.sa->fuse_time) / HZ; ipsec_sa_mod_timer(sa_idx.sa); IPSEC6_DEBUG("set fuse_time = %lu\n", (sa_idx.sa->fuse_time)); } sa_idx.sa->lifetime_c.bytes += totalsize; IPSEC6_DEBUG("sa->bytes=%-9u %-9u\n", /* XXX: %-18Lu */ (__u32)((sa_idx.sa->lifetime_c.bytes) >> 32), (__u32)(sa_idx.sa->lifetime_c.bytes)); if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_s.bytes && sa_idx.sa->lifetime_s.bytes) { sa_idx.sa->state = SADB_SASTATE_DYING; IPSEC6_DEBUG("change sa state DYING\n"); } if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_h.bytes && sa_idx.sa->lifetime_h.bytes) { sa_idx.sa->state = SADB_SASTATE_DEAD; IPSEC6_DEBUG("change sa state DEAD\n"); } unlock_finish: write_unlock_bh(&sa_idx.sa->lock); /* unlock SA */ ipsec_sa_put(sa_idx.sa); finish: return rtn; }