📄 ip_input.c
字号:
if (i < GETIP(q)->ip_len) {
GETIP(q)->ip_len -= i;
GETIP(q)->ip_off += i;
m_adj(q, i);
q->m_pkthdr.csum_flags = 0;
break;
}
nq = q->m_nextpkt;
m->m_nextpkt = nq;
m_freem(q);
}
inserted:
#ifdef IPDIVERT
/*
* Transfer firewall instructions to the fragment structure.
* Any fragment diverting causes the whole packet to divert.
*/
fp->ipq_div_info = *divinfo;
fp->ipq_div_cookie = *divcookie;
*divinfo = 0;
*divcookie = 0;
#endif
/*
* Check for complete reassembly.
*/
next = 0;
for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {
if (GETIP(q)->ip_off != next)
return (0);
next += GETIP(q)->ip_len;
}
/* Make sure the last packet didn't have the IP_MF flag */
if (p->m_flags & M_FRAG)
return (0);
/*
* Reassembly is complete. Make sure the packet is a sane size.
*/
q = fp->ipq_frags;
ip = GETIP(q);
if (next + (IP_VHL_HL(ip->ip_vhl) << 2) > IP_MAXPACKET) {
ipstat.ips_toolong++;
ip_freef(fp);
return (0);
}
/*
* Concatenate fragments.
*/
m = q;
t = m->m_next;
m->m_next = 0;
m_cat(m, t);
nq = q->m_nextpkt;
q->m_nextpkt = 0;
for (q = nq; q != NULL; q = nq) {
nq = q->m_nextpkt;
q->m_nextpkt = NULL;
m->m_pkthdr.csum_flags &= q->m_pkthdr.csum_flags;
m->m_pkthdr.csum_data += q->m_pkthdr.csum_data;
m_cat(m, q);
}
#ifdef IPDIVERT
/*
* Extract firewall instructions from the fragment structure.
*/
*divinfo = fp->ipq_div_info;
*divcookie = fp->ipq_div_cookie;
#endif
/*
* 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_src = fp->ipq_src;
ip->ip_dst = fp->ipq_dst;
remque(fp);
nipq--;
(void) m_free(dtom(fp));
ip_nfragpackets--;
m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2);
m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 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; t; t = t->m_next)
plen += t->m_len;
m->m_pkthdr.len = plen;
}
return (m);
dropfrag:
#ifdef IPDIVERT
*divinfo = 0;
*divcookie = 0;
#endif
ipstat.ips_fragdropped++;
m_freem(m);
return (0);
#undef GETIP
}
/*
* Free a fragment reassembly header and all
* associated datagrams.
*/
static void
ip_freef(fp)
struct ipq *fp;
{
register struct mbuf *q;
while (fp->ipq_frags) {
q = fp->ipq_frags;
fp->ipq_frags = q->m_nextpkt;
m_freem(q);
}
remque(fp);
(void) m_free(dtom(fp));
ip_nfragpackets--;
nipq--;
}
/*
* IP timer processing;
* if a timer expires on a reassembly
* queue, discard it.
*/
void
ip_slowtimo()
{
register struct ipq *fp;
int s = splnet();
int i;
for (i = 0; i < IPREASS_NHASH; i++) {
fp = ipq[i].next;
if (fp == 0)
continue;
while (fp != &ipq[i]) {
--fp->ipq_ttl;
fp = fp->next;
if (fp->prev->ipq_ttl == 0) {
ipstat.ips_fragtimeout++;
ip_freef(fp->prev);
}
}
}
/*
* If we are over the maximum number of fragments
* (due to the limit being lowered), drain off
* enough to get down to the new limit.
*/
for (i = 0; i < IPREASS_NHASH; i++) {
if (ip_maxfragpackets >= 0) {
while ((ip_nfragpackets > ip_maxfragpackets) &&
(ipq[i].next != &ipq[i])) {
ipstat.ips_fragdropped++;
ip_freef(ipq[i].next);
}
}
}
ipflow_slowtimo();
splx(s);
}
/*
* Drain off all datagram fragments.
*/
void
ip_drain()
{
int i;
for (i = 0; i < IPREASS_NHASH; i++) {
while (ipq[i].next != &ipq[i]) {
ipstat.ips_fragdropped++;
ip_freef(ipq[i].next);
}
}
in_rtqdrain();
}
/*
* 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.
*/
static 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_VHL_HL(ip->ip_vhl) << 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 {
if (cnt < IPOPT_OLEN + sizeof(*cp)) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
goto bad;
}
optlen = cp[IPOPT_OLEN];
if (optlen < IPOPT_OLEN + sizeof(*cp) || 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 (optlen < IPOPT_OFFSET + sizeof(*cp)) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
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 = (struct in_ifaddr *)
ifa_ifwithaddr((struct sockaddr *)&ipaddr);
if (ia == 0) {
if (opt == IPOPT_SSRR) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
if (!ip_dosourceroute)
goto nosourcerouting;
/*
* Loose routing, and not at next destination
* yet; nothing to do except forward.
*/
break;
}
off--; /* 0 origin */
if (off > optlen - (int)sizeof(struct in_addr)) {
/*
* End of source route. Should be for us.
*/
if (!ip_acceptsourceroute)
goto nosourcerouting;
save_rte(cp, ip->ip_src);
break;
}
if (!ip_dosourceroute) {
if (ipforwarding) {
char buf[16]; /* aaa.bbb.ccc.ddd\0 */
/*
* Acting as a router, so generate ICMP
*/
nosourcerouting:
strcpy(buf, inet_ntoa(ip->ip_dst));
log(LOG_WARNING,
"attempted source route from %s to %s\n",
inet_ntoa(ip->ip_src), buf);
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
} else {
/*
* Not acting as a router, so silently drop.
*/
ipstat.ips_cantforward++;
m_freem(m);
return (1);
}
}
/*
* locate outgoing interface
*/
(void)memcpy(&ipaddr.sin_addr, cp + off,
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;
(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
sizeof(struct in_addr));
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
/*
* Let ip_intr's mcast routing check handle mcast pkts
*/
forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
break;
case IPOPT_RR:
if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
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 - (int)sizeof(struct in_addr))
break;
(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
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;
}
(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
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_len < 4 || ipt->ipt_len > 40) {
code = (u_char *)&ipt->ipt_len - (u_char *)ip;
goto bad;
}
if (ipt->ipt_ptr < 5) {
code = (u_char *)&ipt->ipt_ptr - (u_char *)ip;
goto bad;
}
if (ipt->ipt_ptr >
ipt->ipt_len - (int)sizeof(int32_t)) {
if (++ipt->ipt_oflw == 0) {
code = (u_char *)&ipt->ipt_ptr -
(u_char *)ip;
goto bad;
}
break;
}
sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
switch (ipt->ipt_flg) {
case IPOPT_TS_TSONLY:
break;
case IPOPT_TS_TSANDADDR:
if (ipt->ipt_ptr - 1 + sizeof(n_time) +
sizeof(struct in_addr) > ipt->ipt_len) {
code = (u_char *)&ipt->ipt_ptr -
(u_char *)ip;
goto bad;
}
ipaddr.sin_addr = dst;
ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
m->m_pkthdr.rcvif);
if (ia == 0)
continue;
(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
sizeof(struct in_addr));
ipt->ipt_ptr += sizeof(struct in_addr);
break;
case IPOPT_TS_PRESPEC:
if (ipt->ipt_ptr - 1 + sizeof(n_time) +
sizeof(struct in_addr) > ipt->ipt_len) {
code = (u_char *)&ipt->ipt_ptr -
(u_char *)ip;
goto bad;
}
(void)memcpy(&ipaddr.sin_addr, sin,
sizeof(struct in_addr));
if (ifa_ifwithaddr((SA)&ipaddr) == 0)
continue;
ipt->ipt_ptr += sizeof(struct in_addr);
break;
default:
/* XXX can't take &ipt->ipt_flg */
code = (u_char *)&ipt->ipt_ptr -
(u_char *)ip + 1;
goto bad;
}
ntime = iptime();
(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,
sizeof(n_time));
ipt->ipt_ptr += sizeof(n_time);
}
}
if (forward && ipforwarding) {
ip_forward(m, 1);
return (1);
}
return (0);
bad:
icmp_error(m, type, code, 0, 0);
ipstat.ips_badoptions++;
return (1);
}
/*
* Given address of next destination (final or next hop),
* return internet address info of interface to be used to get there.
*/
static struct in_ifaddr *
ip_rtaddr(dst)
struct in_addr dst;
{
register struct sockaddr_in *sin;
sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
if (ipforward_rt.ro_rt) {
RTFREE(ipforward_rt.ro_rt);
ipforward_rt.ro_rt = 0;
}
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = dst;
rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
}
if (ipforward_rt.ro_rt == 0)
return ((struct in_ifaddr *)0);
return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
}
/*
* Save incoming source route for use in replies,
* to be picked up later by ip_srcroute if the receiver is interested.
*/
void
save_rte(option, dst)
u_char *option;
struct in_addr dst;
{
unsigned olen;
olen = option[IPOPT_OLEN];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -