// SPDX-License-Identifier: GPL-2.0 /* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * The IP fragmentation functionality. * * Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG> * Alan Cox <alan@lxorguk.ukuu.org.uk> * * Fixes: * Alan Cox : Split from ip.c , see ip_input.c for history. * David S. Miller : Begin massive cleanup... * Andi Kleen : Add sysctls. * xxxx : Overlapfrag bug. * Ultima : ip_expire() kernel panic. * Bill Hawes : Frag accounting and evictor fixes. * John McDonald : 0 length frag bug. * Alexey Kuznetsov: SMP races, threading, cleanup. * Patrick McHardy : LRU queue of frag heads for evictor.
*/
/* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6 * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c * as well. Or notify me, at least. --ANK
*/ staticconstchar ip_frag_cache_name[] = "ip4-frags";
/* Describe an entry in the "incomplete datagrams" queue. */ struct ipq { struct inet_frag_queue q;
u8 ecn; /* RFC3168 support */
u16 max_df_size; /* largest frag with DF set seen */ int iif; unsignedint rid; struct inet_peer *peer;
};
if (!(qp->q.flags & INET_FRAG_FIRST_IN)) goto out;
/* sk_buff::dev and sk_buff::rbnode are unionized. So we * pull the head out of the tree in order to be able to * deal with head->dev.
*/
head = inet_frag_pull_head(&qp->q); if (!head) goto out;
head->dev = dev_get_by_index_rcu(net, qp->iif); if (!head->dev) goto out;
/* skb has no dst, perform route lookup again */
iph = ip_hdr(head);
reason = ip_route_input_noref(head, iph->daddr, iph->saddr,
ip4h_dscp(iph), head->dev); if (reason) goto out;
/* Only an end host needs to send an ICMP * "Fragment Reassembly Timeout" message, per RFC792.
*/
reason = SKB_DROP_REASON_FRAG_REASM_TIMEOUT; if (frag_expire_skip_icmp(qp->q.key.v4.user) &&
(skb_rtable(head)->rt_type != RTN_LOCAL)) goto out;
/* Find the correct entry in the "incomplete datagrams" queue for * this IP datagram, and create new one, if nothing is found.
*/ staticstruct ipq *ip_find(struct net *net, struct iphdr *iph,
u32 user, int vif)
{ struct frag_v4_compare_key key = {
.saddr = iph->saddr,
.daddr = iph->daddr,
.user = user,
.vif = vif,
.id = iph->id,
.protocol = iph->protocol,
}; struct inet_frag_queue *q;
q = inet_frag_find(net->ipv4.fqdir, &key); if (!q) return NULL;
return container_of(q, struct ipq, q);
}
/* Is the fragment too far ahead to be part of ipq? */ staticint ip_frag_too_far(struct ipq *qp)
{ struct inet_peer *peer = qp->peer; unsignedint max = qp->q.fqdir->max_dist; unsignedint start, end;
int rc;
if (!peer || !max) return 0;
start = qp->rid;
end = atomic_inc_return(&peer->rid);
qp->rid = end;
rc = qp->q.fragments_tail && (end - start) > max;
if (rc)
__IP_INC_STATS(qp->q.fqdir->net, IPSTATS_MIB_REASMFAILS);
/* Determine the position of this fragment. */
end = offset + skb->len - skb_network_offset(skb) - ihl;
err = -EINVAL;
/* Is this the final fragment? */ if ((flags & IP_MF) == 0) { /* If we already have some bits beyond end * or have different end, the segment is corrupted.
*/ if (end < qp->q.len ||
((qp->q.flags & INET_FRAG_LAST_IN) && end != qp->q.len)) goto discard_qp;
qp->q.flags |= INET_FRAG_LAST_IN;
qp->q.len = end;
} else { if (end&7) {
end &= ~7; if (skb->ip_summed != CHECKSUM_UNNECESSARY)
skb->ip_summed = CHECKSUM_NONE;
} if (end > qp->q.len) { /* Some bits beyond end -> corruption. */ if (qp->q.flags & INET_FRAG_LAST_IN) goto discard_qp;
qp->q.len = end;
}
} if (end == offset) goto discard_qp;
err = -ENOMEM; if (!pskb_pull(skb, skb_network_offset(skb) + ihl)) goto discard_qp;
err = pskb_trim_rcsum(skb, end - offset); if (err) goto discard_qp;
/* Note : skb->rbnode and skb->dev share the same location. */
dev = skb->dev; /* Makes sure compiler wont do silly aliasing games */
barrier();
/* Build a new IP datagram from all its fragments. */ staticint ip_frag_reasm(struct ipq *qp, struct sk_buff *skb, struct sk_buff *prev_tail, struct net_device *dev, int *refs)
{ struct net *net = qp->q.fqdir->net; struct iphdr *iph; void *reasm_data; int len, err;
u8 ecn;
/* When we set IP_DF on a refragmented skb we must also force a * call to ip_fragment to avoid forwarding a DF-skb of size s while * original sender only sent fragments of size f (where f < s). * * We only set DF/IPSKB_FRAG_PMTU if such DF fragment was the largest * frag seen to avoid sending tiny DF-fragments in case skb was built * from one very small df-fragment and one large non-df frag.
*/ if (qp->max_df_size == qp->q.max_size) {
IPCB(skb)->flags |= IPSKB_FRAG_PMTU;
iph->frag_off = htons(IP_DF);
} else {
iph->frag_off = 0;
}
out_nomem:
net_dbg_ratelimited("queue_glue: no memory for gluing queue %p\n", qp);
err = -ENOMEM; goto out_fail;
out_oversize:
net_info_ratelimited("Oversized IP packet from %pI4\n", &qp->q.key.v4.saddr);
out_fail:
__IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); return err;
}
/* Process an incoming IP datagram fragment. */ int ip_defrag(struct net *net, struct sk_buff *skb, u32 user)
{ struct net_device *dev; struct ipq *qp; int vif;
staticint __net_init ipv4_frags_init_net(struct net *net)
{ int res;
res = fqdir_init(&net->ipv4.fqdir, &ip4_frags, net); if (res < 0) return res; /* Fragment cache limits. * * The fragment memory accounting code, (tries to) account for * the real memory usage, by measuring both the size of frag * queue struct (inet_frag_queue (ipv4:ipq/ipv6:frag_queue)) * and the SKB's truesize. * * A 64K fragment consumes 129736 bytes (44*2944)+200 * (1500 truesize == 2944, sizeof(struct ipq) == 200) * * We will commit 4MB at one time. Should we cross that limit * we will prune down to 3MB, making room for approx 8 big 64K * fragments 8x128k.
*/
net->ipv4.fqdir->high_thresh = 4 * 1024 * 1024;
net->ipv4.fqdir->low_thresh = 3 * 1024 * 1024; /* * Important NOTE! Fragment queue must be destroyed before MSL expires. * RFC791 is wrong proposing to prolongate timer each fragment arrival * by TTL.
*/
net->ipv4.fqdir->timeout = IP_FRAG_TIME;
net->ipv4.fqdir->max_dist = 64;
res = ip4_frags_ns_ctl_register(net); if (res < 0)
fqdir_exit(net->ipv4.fqdir); return res;
}
staticvoid __net_exit ipv4_frags_pre_exit_net(struct net *net)
{
fqdir_pre_exit(net->ipv4.fqdir);
}
staticvoid __net_exit ipv4_frags_exit_net(struct net *net)
{
ip4_frags_ns_ctl_unregister(net);
fqdir_exit(net->ipv4.fqdir);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.