📄 ip_input.c
字号:
ipstat.ips_toosmall++;
return;
}
ip = mtod(m, struct ip *);
}
/*
* Look for queue of fragments
* of this datagram.
*/
ipq_lock();
for (fp = ipq.lh_first; fp != NULL; fp = fp->ipq_q.le_next)
if (ip->ip_id == fp->ipq_id &&
ip->ip_src.s_addr == fp->ipq_src.s_addr &&
ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
ip->ip_p == fp->ipq_p)
goto found;
fp = 0;
found:
/*
* Adjust ip_len to not reflect header,
* set ipqe_mff if more fragments are expected,
* convert offset of this to bytes.
*/
ip->ip_len -= hlen;
mff = (ip->ip_off & IP_MF) != 0;
if (mff) {
/*
* Make sure that fragments have a data length
* that's a non-zero multiple of 8 bytes.
*/
if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {
ipstat.ips_badfrags++;
ipq_unlock();
goto bad;
}
}
ip->ip_off <<= 3;
/*
* If datagram marked as having more fragments
* or if this is not the first fragment,
* attempt reassembly; if it succeeds, proceed.
*/
if (mff || ip->ip_off) {
ipstat.ips_fragments++;
if (ip_frags + 1 > ip_maxqueue) {
ip_flush();
ipstat.ips_rcvmemdrop++;
ipq_unlock();
goto bad;
}
MALLOC(ipqe, struct ipqent *, sizeof (struct ipqent),
M_IPQ, M_NOWAIT);
if (ipqe == NULL) {
ipstat.ips_rcvmemdrop++;
ipq_unlock();
goto bad;
}
ip_frags++;
ipqe->ipqe_mff = mff;
ipqe->ipqe_ip = ip;
ip = ip_reass(ipqe, fp);
if (ip == 0) {
ipq_unlock();
return;
}
ipstat.ips_reassembled++;
m = dtom(ip);
hlen = ip->ip_hl << 2;
} else
if (fp)
ip_freef(fp);
ipq_unlock();
} else
ip->ip_len -= hlen;
/*
* Switch out to protocol's input routine.
*/
ipstat.ips_delivered++;
(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen, NULL, 0);
return;
bad:
m_freem(m);
}
struct in_ifaddr *
in_iawithaddr(ina, m)
struct in_addr ina;
register struct mbuf *m;
{
register struct in_ifaddr *ia;
for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) {
if ((ina.s_addr == ia->ia_addr.sin_addr.s_addr) ||
((ia->ia_ifp->if_flags & (IFF_LOOPBACK|IFF_LINK1)) ==
(IFF_LOOPBACK|IFF_LINK1) &&
ia->ia_subnet == (ina.s_addr & ia->ia_subnetmask)))
return ia;
if (m && ((ip_directedbcast == 0) || (ip_directedbcast &&
ia->ia_ifp == m->m_pkthdr.rcvif)) &&
(ia->ia_ifp->if_flags & IFF_BROADCAST)) {
if (ina.s_addr == ia->ia_broadaddr.sin_addr.s_addr ||
ina.s_addr == ia->ia_netbroadcast.s_addr ||
/*
* Look for all-0's host part (old broadcast addr),
* either for subnet or net.
*/
ina.s_addr == ia->ia_subnet ||
ina.s_addr == ia->ia_net) {
/* Make sure M_BCAST is set */
m->m_flags |= M_BCAST;
return ia;
}
}
}
return NULL;
}
/*
* Take incoming datagram fragment and try to
* reassemble it into whole datagram. If a chain for
* reassembly of this datagram already exists, then it
* is given as fp; otherwise have to make a chain.
*/
struct ip *
ip_reass(ipqe, fp)
register struct ipqent *ipqe;
register struct ipq *fp;
{
register struct mbuf *m = dtom(ipqe->ipqe_ip);
register struct ipqent *nq, *p, *q;
struct ip *ip;
struct mbuf *t;
int hlen = ipqe->ipqe_ip->ip_hl << 2;
int i, next;
/*
* Presence of header sizes in mbufs
* would confuse code below.
*/
m->m_data += hlen;
m->m_len -= hlen;
/*
* If first fragment to arrive, create a reassembly queue.
*/
if (fp == 0) {
if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
goto dropfrag;
fp = mtod(t, struct ipq *);
LIST_INSERT_HEAD(&ipq, fp, ipq_q);
fp->ipq_ttl = IPFRAGTTL;
fp->ipq_p = ipqe->ipqe_ip->ip_p;
fp->ipq_id = ipqe->ipqe_ip->ip_id;
LIST_INIT(&fp->ipq_fragq);
fp->ipq_src = ipqe->ipqe_ip->ip_src;
fp->ipq_dst = ipqe->ipqe_ip->ip_dst;
p = NULL;
goto insert;
}
/*
* Find a segment which begins after this one does.
*/
for (p = NULL, q = fp->ipq_fragq.lh_first; q != NULL;
p = q, q = q->ipqe_q.le_next)
if (q->ipqe_ip->ip_off > ipqe->ipqe_ip->ip_off)
break;
/*
* If there is a preceding segment, it may provide some of
* our data already. If so, drop the data from the incoming
* segment. If it provides all of our data, drop us.
*/
if (p != NULL) {
i = p->ipqe_ip->ip_off + p->ipqe_ip->ip_len -
ipqe->ipqe_ip->ip_off;
if (i > 0) {
if (i >= ipqe->ipqe_ip->ip_len)
goto dropfrag;
m_adj(dtom(ipqe->ipqe_ip), i);
ipqe->ipqe_ip->ip_off += i;
ipqe->ipqe_ip->ip_len -= i;
}
}
/*
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
*/
for (; q != NULL && ipqe->ipqe_ip->ip_off + ipqe->ipqe_ip->ip_len >
q->ipqe_ip->ip_off; q = nq) {
i = (ipqe->ipqe_ip->ip_off + ipqe->ipqe_ip->ip_len) -
q->ipqe_ip->ip_off;
if (i < q->ipqe_ip->ip_len) {
q->ipqe_ip->ip_len -= i;
q->ipqe_ip->ip_off += i;
m_adj(dtom(q->ipqe_ip), i);
break;
}
nq = q->ipqe_q.le_next;
m_freem(dtom(q->ipqe_ip));
LIST_REMOVE(q, ipqe_q);
FREE(q, M_IPQ);
ip_frags--;
}
insert:
/*
* Stick new segment in its place;
* check for complete reassembly.
*/
if (p == NULL) {
LIST_INSERT_HEAD(&fp->ipq_fragq, ipqe, ipqe_q);
} else {
LIST_INSERT_AFTER(p, ipqe, ipqe_q);
}
next = 0;
for (p = NULL, q = fp->ipq_fragq.lh_first; q != NULL;
p = q, q = q->ipqe_q.le_next) {
if (q->ipqe_ip->ip_off != next)
return (0);
next += q->ipqe_ip->ip_len;
}
if (p->ipqe_mff)
return (0);
/*
* Reassembly is complete. Check for a bogus message size and
* concatenate fragments.
*/
q = fp->ipq_fragq.lh_first;
ip = q->ipqe_ip;
if ((next + (ip->ip_hl << 2)) > IP_MAXPACKET) {
ipstat.ips_toolong++;
ip_freef(fp);
return (0);
}
m = dtom(q->ipqe_ip);
t = m->m_next;
m->m_next = 0;
m_cat(m, t);
nq = q->ipqe_q.le_next;
FREE(q, M_IPQ);
ip_frags--;
for (q = nq; q != NULL; q = nq) {
t = dtom(q->ipqe_ip);
nq = q->ipqe_q.le_next;
FREE(q, M_IPQ);
ip_frags--;
m_cat(m, t);
}
/*
* Create header for new ip packet by
* modifying header of first packet;
* dequeue and discard fragment reassembly header.
* Make header visible.
*/
ip->ip_len = next;
ip->ip_ttl = 0; /* xxx */
ip->ip_sum = 0;
ip->ip_src = fp->ipq_src;
ip->ip_dst = fp->ipq_dst;
LIST_REMOVE(fp, ipq_q);
(void) m_free(dtom(fp));
m->m_len += (ip->ip_hl << 2);
m->m_data -= (ip->ip_hl << 2);
/* some debugging cruft by sklower, below, will go away soon */
if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
register int plen = 0;
for (t = m; m; m = m->m_next)
plen += m->m_len;
t->m_pkthdr.len = plen;
}
return (ip);
dropfrag:
ipstat.ips_fragdropped++;
m_freem(m);
FREE(ipqe, M_IPQ);
ip_frags--;
return (0);
}
/*
* Free a fragment reassembly header and all
* associated datagrams.
*/
void
ip_freef(fp)
struct ipq *fp;
{
register struct ipqent *q, *p;
for (q = fp->ipq_fragq.lh_first; q != NULL; q = p) {
p = q->ipqe_q.le_next;
m_freem(dtom(q->ipqe_ip));
LIST_REMOVE(q, ipqe_q);
FREE(q, M_IPQ);
ip_frags--;
}
LIST_REMOVE(fp, ipq_q);
(void) m_free(dtom(fp));
}
/*
* IP timer processing;
* if a timer expires on a reassembly
* queue, discard it.
*/
void
ip_slowtimo()
{
register struct ipq *fp, *nfp;
int s = splsoftnet();
ipq_lock();
for (fp = ipq.lh_first; fp != NULL; fp = nfp) {
nfp = fp->ipq_q.le_next;
if (--fp->ipq_ttl == 0) {
ipstat.ips_fragtimeout++;
ip_freef(fp);
}
}
ipq_unlock();
splx(s);
}
/*
* Drain off all datagram fragments.
*/
void
ip_drain()
{
if (ipq_lock_try() == 0)
return;
while (ipq.lh_first != NULL) {
ipstat.ips_fragdropped++;
ip_freef(ipq.lh_first);
}
ipq_unlock();
}
/*
* Flush a bunch of datagram fragments, till we are down to 75%.
*/
void
ip_flush()
{
int max = 50;
/* ipq already locked */
while (ipq.lh_first != NULL && ip_frags > ip_maxqueue * 3 / 4 && --max) {
ipstat.ips_fragdropped++;
ip_freef(ipq.lh_first);
}
}
/*
* Do option processing on a datagram,
* possibly discarding it if bad options are encountered,
* or forwarding it if source-routed.
* Returns 1 if packet has been forwarded/freed,
* 0 if the packet should be processed further.
*/
int
ip_dooptions(m)
struct mbuf *m;
{
register struct ip *ip = mtod(m, struct ip *);
register u_char *cp;
register struct ip_timestamp *ipt;
register struct in_ifaddr *ia;
int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
struct in_addr *sin, dst;
n_time ntime;
dst = ip->ip_dst;
cp = (u_char *)(ip + 1);
cnt = (ip->ip_hl << 2) - sizeof (struct ip);
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP)
optlen = 1;
else {
optlen = cp[IPOPT_OLEN];
if (optlen <= 0 || optlen > cnt) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
goto bad;
}
}
switch (opt) {
default:
break;
/*
* Source routing with record.
* Find interface with current destination address.
* If none on this machine then drop if strictly routed,
* or do nothing if loosely routed.
* Record interface address and bring up next address
* component. If strictly routed make sure next
* address is on directly accessible net.
*/
case IPOPT_LSRR:
case IPOPT_SSRR:
if (!ip_dosourceroute) {
#ifndef __ECOS
char buf[4*sizeof "123"];
strcpy(buf, inet_ntoa(ip->ip_dst));
log(LOG_WARNING,
"attempted source route from %s to %s\n",
inet_ntoa(ip->ip_src), buf);
#endif
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
ipaddr.sin_addr = ip->ip_dst;
ia = ifatoia(ifa_ifwithaddr(sintosa(&ipaddr)));
if (ia == 0) {
if (opt == IPOPT_SSRR) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
/*
* Loose routing, and not at next destination
* yet; nothing to do except forward.
*/
break;
}
off--; /* 0 origin */
if (off > optlen - sizeof(struct in_addr)) {
/*
* End of source route. Should be for us.
*/
save_rte(cp, ip->ip_src);
break;
}
/*
* locate outgoing interface
*/
bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
sizeof(ipaddr.sin_addr));
if (opt == IPOPT_SSRR) {
#define INA struct in_ifaddr *
#define SA struct sockaddr *
if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
ia = (INA)ifa_ifwithnet((SA)&ipaddr);
} else
ia = ip_rtaddr(ipaddr.sin_addr);
if (ia == 0) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
ip->ip_dst = ipaddr.sin_addr;
bcopy((caddr_t)&ia->ia_addr.sin_addr,
(caddr_t)(cp + off), sizeof(struct in_addr));
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
/*
* Let ip_intr's mcast routing check handle mcast pkts
*/
forward = !IN_MULTICAST(ip->ip_dst.s_addr);
break;
case IPOPT_RR:
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
/*
* If no space remains, ignore.
*/
off--; /* 0 origin */
if (off > optlen - sizeof(struct in_addr))
break;
bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
sizeof(ipaddr.sin_addr));
/*
* locate outgoing interface; if we're the destination,
* use the incoming interface (should be same).
*/
if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
(ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_HOST;
goto bad;
}
bcopy((caddr_t)&ia->ia_addr.sin_addr,
(caddr_t)(cp + off), sizeof(struct in_addr));
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
break;
case IPOPT_TS:
code = cp - (u_char *)ip;
ipt = (struct ip_timestamp *)cp;
if (ipt->ipt_ptr < 5 || ipt->ipt_len < 5)
goto bad;
if (ipt->ipt_ptr - 1 + sizeof(n_time) > ipt->ipt_len) {
if (++ipt->ipt_oflw == 0)
goto bad;
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -