/* ip_fragment.c * * Copyright (C) 2002 Ulrich Marx * 2003 Mathias Koehrer * 2003-2005 Jan Kiszka * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_ADDON_PROXY) #include #endif /* CONFIG_XENO_DRIVERS_NET_ADDON_PROXY */ /* * This defined sets the number of incoming fragmented IP messages that * can be handled in parallel. */ #define COLLECTOR_COUNT 10 struct ip_collector { int in_use; __u32 saddr; __u32 daddr; __u16 id; __u8 protocol; struct rtskb_queue frags; struct rtsocket *sock; unsigned int buf_size; }; static struct ip_collector collector[COLLECTOR_COUNT]; static void alloc_collector(struct rtskb *skb, struct rtsocket *sock) { int i; rtdm_lockctx_t context; struct ip_collector *p_coll; struct iphdr *iph = skb->nh.iph; /* * Find a free collector * * Note: We once used to clean up probably outdated chains, but the * algorithm was not stable enough and could cause incorrect drops even * under medium load. If we run in overload, we will loose data anyhow. * What we should do in the future is to account collectors per socket or * socket owner and set quotations. * Garbage collection is now performed only on socket close. */ for (i = 0; i < COLLECTOR_COUNT; i++) { p_coll = &collector[i]; rtdm_lock_get_irqsave(&p_coll->frags.lock, context); if (!p_coll->in_use) { p_coll->in_use = 1; p_coll->buf_size = skb->len; p_coll->frags.first = skb; p_coll->frags.last = skb; p_coll->saddr = iph->saddr; p_coll->daddr = iph->daddr; p_coll->id = iph->id; p_coll->protocol = iph->protocol; p_coll->sock = sock; rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); return; } rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); } rtdm_printk("RTnet: IP fragmentation - no collector available\n"); kfree_rtskb(skb); } /* * Return a pointer to the collector that holds the message which * fits to the iphdr of the passed rtskb. * */ static struct rtskb *add_to_collector(struct rtskb *skb, unsigned int offset, int more_frags) { int i, err; rtdm_lockctx_t context; struct ip_collector *p_coll; struct iphdr *iph = skb->nh.iph; struct rtskb *first_skb; /* Search in existing collectors */ for (i = 0; i < COLLECTOR_COUNT; i++) { p_coll = &collector[i]; rtdm_lock_get_irqsave(&p_coll->frags.lock, context); if (p_coll->in_use && (iph->saddr == p_coll->saddr) && (iph->daddr == p_coll->daddr) && (iph->id == p_coll->id) && (iph->protocol == p_coll->protocol)) { first_skb = p_coll->frags.first; /* Acquire the rtskb at the expense of the protocol pool */ if (rtskb_acquire(skb, &p_coll->sock->skb_pool) != 0) { /* We have to drop this fragment => clean up the whole chain */ p_coll->in_use = 0; rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); #ifdef FRAG_DBG rtdm_printk( "RTnet: Compensation pool empty - IP fragments " "dropped (saddr:%x, daddr:%x)\n", iph->saddr, iph->daddr); #endif kfree_rtskb(first_skb); kfree_rtskb(skb); return NULL; } /* Optimized version of __rtskb_queue_tail */ skb->next = NULL; p_coll->frags.last->next = skb; p_coll->frags.last = skb; /* Extend the chain */ first_skb->chain_end = skb; /* Sanity check: unordered fragments are not allowed! */ if (offset != p_coll->buf_size) { /* We have to drop this fragment => clean up the whole chain */ p_coll->in_use = 0; skb = first_skb; rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); break; /* leave the for loop */ } p_coll->buf_size += skb->len; if (!more_frags) { p_coll->in_use = 0; err = rt_socket_reference(p_coll->sock); rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); if (err < 0) { kfree_rtskb(first_skb); return NULL; } return first_skb; } else { rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); return NULL; } } rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); } #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_ADDON_PROXY) if (rt_ip_fallback_handler) { __rtskb_push(skb, iph->ihl * 4); rt_ip_fallback_handler(skb); return NULL; } #endif #ifdef FRAG_DBG rtdm_printk("RTnet: Unordered IP fragment (saddr:%x, daddr:%x)" " - dropped\n", iph->saddr, iph->daddr); #endif kfree_rtskb(skb); return NULL; } /* * Cleans up all collectors referring to the specified socket. * This is now the only kind of garbage collection we do. */ void rt_ip_frag_invalidate_socket(struct rtsocket *sock) { int i; rtdm_lockctx_t context; struct ip_collector *p_coll; for (i = 0; i < COLLECTOR_COUNT; i++) { p_coll = &collector[i]; rtdm_lock_get_irqsave(&p_coll->frags.lock, context); if ((p_coll->in_use) && (p_coll->sock == sock)) { p_coll->in_use = 0; kfree_rtskb(p_coll->frags.first); } rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); } } EXPORT_SYMBOL_GPL(rt_ip_frag_invalidate_socket); /* * Cleans up all existing collectors */ static void cleanup_all_collectors(void) { int i; rtdm_lockctx_t context; struct ip_collector *p_coll; for (i = 0; i < COLLECTOR_COUNT; i++) { p_coll = &collector[i]; rtdm_lock_get_irqsave(&p_coll->frags.lock, context); if (p_coll->in_use) { p_coll->in_use = 0; kfree_rtskb(p_coll->frags.first); } rtdm_lock_put_irqrestore(&p_coll->frags.lock, context); } } /* * This function returns an rtskb that contains the complete, accumulated IP message. * If not all fragments of the IP message have been received yet, it returns NULL * Note: the IP header must have already been pulled from the rtskb! * */ struct rtskb *rt_ip_defrag(struct rtskb *skb, struct rtinet_protocol *ipprot) { unsigned int more_frags; unsigned int offset; struct rtsocket *sock; struct iphdr *iph = skb->nh.iph; int ret; /* Parse the IP header */ offset = ntohs(iph->frag_off); more_frags = offset & IP_MF; offset &= IP_OFFSET; offset <<= 3; /* offset is in 8-byte chunks */ /* First fragment? */ if (offset == 0) { /* Get the destination socket */ if ((sock = ipprot->dest_socket(skb)) == NULL) { #if IS_ENABLED(CONFIG_XENO_DRIVERS_NET_ADDON_PROXY) if (rt_ip_fallback_handler) { __rtskb_push(skb, iph->ihl * 4); rt_ip_fallback_handler(skb); return NULL; } #endif /* Drop the rtskb */ kfree_rtskb(skb); return NULL; } /* Acquire the rtskb, to unlock the device skb pool */ ret = rtskb_acquire(skb, &sock->skb_pool); if (ret != 0) { /* Drop the rtskb */ kfree_rtskb(skb); } else { /* Allocates a new collector */ alloc_collector(skb, sock); } /* Packet is queued or freed, socket can be released */ rt_socket_dereference(sock); return NULL; } else { /* Add to an existing collector */ return add_to_collector(skb, offset, more_frags); } } int __init rt_ip_fragment_init(void) { int i; /* Probably not needed (static variable...) */ memset(collector, 0, sizeof(collector)); for (i = 0; i < COLLECTOR_COUNT; i++) rtdm_lock_init(&collector[i].frags.lock); return 0; } void rt_ip_fragment_cleanup(void) { cleanup_all_collectors(); }