/* ip_input.c 6.3 84/05/25 */
#include "../h/protosw.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_pcb.h"
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"
#include "../netinet/ip_var.h"
#include "../netinet/ip_icmp.h"
#include "../netinet/tcp.h"
u_char ip_protox
[IPPROTO_MAX
];
int ipqmaxlen
= IFQ_MAXLEN
;
struct ifnet
*ifinet
; /* first inet interface */
* IP initialization: fill in IP protocol switch table.
* All protocols not implemented in kernel go to raw IP protocol handler.
register struct protosw
*pr
;
pr
= pffindproto(PF_INET
, IPPROTO_RAW
);
for (i
= 0; i
< IPPROTO_MAX
; i
++)
ip_protox
[i
] = pr
- inetsw
;
for (pr
= inetdomain
.dom_protosw
;
pr
<= inetdomain
.dom_protoswNPROTOSW
; pr
++)
if (pr
->pr_family
== PF_INET
&&
pr
->pr_protocol
&& pr
->pr_protocol
!= IPPROTO_RAW
)
ip_protox
[pr
->pr_protocol
] = pr
- inetsw
;
ipq
.next
= ipq
.prev
= &ipq
;
ip_id
= time
.tv_sec
& 0xffff;
ipintrq
.ifq_maxlen
= ipqmaxlen
;
ifinet
= if_ifwithaf(AF_INET
);
struct sockaddr_in ipaddr
= { AF_INET
};
* Ip input routine. Checksum and byte swap header. If fragmented
* try to reassamble. If complete and fragment queue exists, discard.
* Process options. Pass to next level.
* Get next datagram off input queue and get IP header
if ((m
->m_off
> MMAXOFF
|| m
->m_len
< sizeof (struct ip
)) &&
(m
= m_pullup(m
, sizeof (struct ip
))) == 0) {
ip
= mtod(m
, struct ip
*);
if ((hlen
= ip
->ip_hl
<< 2) > m
->m_len
) {
if ((m
= m_pullup(m
, hlen
)) == 0) {
ip
= mtod(m
, struct ip
*);
if (ip
->ip_sum
= in_cksum(m
, hlen
)) {
* Convert fields to host representation.
ip
->ip_len
= ntohs((u_short
)ip
->ip_len
);
ip
->ip_id
= ntohs(ip
->ip_id
);
ip
->ip_off
= ntohs((u_short
)ip
->ip_off
);
* Check that the amount of data in the buffers
* is as at least much as the IP header would have us expect.
* Trim mbufs if longer than we expect.
* Drop packet if shorter than we expect.
* Process options and, if not destined for us,
* ship it on. ip_dooptions returns 1 when an
* error was detected (causing an icmp message
if (hlen
> sizeof (struct ip
) && ip_dooptions(ip
))
* Fast check on the first internet
sin
= (struct sockaddr_in
*)&ifinet
->if_addr
;
if (sin
->sin_addr
.s_addr
== ip
->ip_dst
.s_addr
)
sin
= (struct sockaddr_in
*)&ifinet
->if_broadaddr
;
if ((ifinet
->if_flags
& IFF_BROADCAST
) &&
sin
->sin_addr
.s_addr
== ip
->ip_dst
.s_addr
)
* Diskless machines don't initially know
* their address, so take packets from them
* if we're acting as a network disk server.
if (in_netof(ip
->ip_dst
) == INADDR_ANY
&&
(in_netof(ip
->ip_src
) == INADDR_ANY
&&
in_lnaof(ip
->ip_src
) != INADDR_ANY
))
ipaddr
.sin_addr
= ip
->ip_dst
;
if (if_ifwithaddr((struct sockaddr
*)&ipaddr
) == 0) {
* Look for queue of fragments
for (fp
= ipq
.next
; fp
!= &ipq
; fp
= fp
->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
&&
* Adjust ip_len to not reflect header,
* set ip_mff if more fragments are expected,
* convert offset of this to bytes.
((struct ipasfrag
*)ip
)->ipf_mff
= 0;
((struct ipasfrag
*)ip
)->ipf_mff
= 1;
* If datagram marked as having more fragments
* or if this is not the first fragment,
* attempt reassembly; if it succeeds, proceed.
if (((struct ipasfrag
*)ip
)->ipf_mff
|| ip
->ip_off
) {
ip
= ip_reass((struct ipasfrag
*)ip
, fp
);
* Switch out to protocol's input routine.
(*inetsw
[ip_protox
[ip
->ip_p
]].pr_input
)(m
);
* 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.
register struct ipasfrag
*ip
;
register struct mbuf
*m
= dtom(ip
);
register struct ipasfrag
*q
;
int hlen
= ip
->ip_hl
<< 2;
* Presence of header sizes in mbufs
* would confuse code below.
* If first fragment to arrive, create a reassembly queue.
if ((t
= m_get(M_WAIT
, MT_FTABLE
)) == NULL
)
fp
= mtod(t
, struct ipq
*);
fp
->ipq_next
= fp
->ipq_prev
= (struct ipasfrag
*)fp
;
fp
->ipq_src
= ((struct ip
*)ip
)->ip_src
;
fp
->ipq_dst
= ((struct ip
*)ip
)->ip_dst
;
q
= (struct ipasfrag
*)fp
;
* Find a segment which begins after this one does.
for (q
= fp
->ipq_next
; q
!= (struct ipasfrag
*)fp
; q
= q
->ipf_next
)
if (q
->ip_off
> ip
->ip_off
)
* 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 (q
->ipf_prev
!= (struct ipasfrag
*)fp
) {
i
= q
->ipf_prev
->ip_off
+ q
->ipf_prev
->ip_len
- ip
->ip_off
;
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
while (q
!= (struct ipasfrag
*)fp
&& ip
->ip_off
+ ip
->ip_len
> q
->ip_off
) {
i
= (ip
->ip_off
+ ip
->ip_len
) - q
->ip_off
;
m_freem(dtom(q
->ipf_prev
));
* Stick new segment in its place;
* check for complete reassembly.
for (q
= fp
->ipq_next
; q
!= (struct ipasfrag
*)fp
; q
= q
->ipf_next
) {
if (q
->ipf_prev
->ipf_mff
)
* Reassembly is complete; concatenate fragments.
while (q
!= (struct ipasfrag
*)fp
) {
* Create header for new ip packet by
* modifying header of first packet;
* dequeue and discard fragment reassembly header.
((struct ip
*)ip
)->ip_src
= fp
->ipq_src
;
((struct ip
*)ip
)->ip_dst
= fp
->ipq_dst
;
m
->m_len
+= sizeof (struct ipasfrag
);
m
->m_off
-= sizeof (struct ipasfrag
);
return ((struct ip
*)ip
);
* Free a fragment reassembly header and all
register struct ipasfrag
*q
, *p
;
for (q
= fp
->ipq_next
; q
!= (struct ipasfrag
*)fp
; q
= p
) {
* Put an ip fragment on a reassembly chain.
* Like insque, but pointers in middle of structure.
register struct ipasfrag
*p
, *prev
;
p
->ipf_next
= prev
->ipf_next
;
prev
->ipf_next
->ipf_prev
= p
;
* To ip_enq as remque is to insque.
register struct ipasfrag
*p
;
p
->ipf_prev
->ipf_next
= p
->ipf_next
;
p
->ipf_next
->ipf_prev
= p
->ipf_prev
;
* if a timer expires on a reassembly
if (fp
->prev
->ipq_ttl
== 0)
* Drain off all datagram fragments.
* Do option processing on a datagram,
* possibly discarding it if bad options
int opt
, optlen
, cnt
, code
, type
;
register struct ip_timestamp
*ipt
;
register struct ifnet
*ifp
;
cnt
= (ip
->ip_hl
<< 2) - sizeof (struct ip
);
for (; cnt
> 0; cnt
-= optlen
, cp
+= optlen
) {
* 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 on directly accessible net.
if (cp
[2] < 4 || cp
[2] > optlen
- (sizeof (long) - 1))
sin
= (struct in_addr
*)(cp
+ cp
[2]);
ifp
= if_ifwithaddr((struct sockaddr
*)&ipaddr
);
type
= ICMP_UNREACH
, code
= ICMP_UNREACH_SRCFAIL
;
t
= ip
->ip_dst
; ip
->ip_dst
= *sin
; *sin
= t
;
if (cp
[2] > optlen
- (sizeof (long) - 1))
if_ifonnetof(in_netof(ip
->ip_dst
)) == 0)
code
= cp
- (u_char
*)ip
;
ipt
= (struct ip_timestamp
*)cp
;
if (ipt
->ipt_ptr
> ipt
->ipt_len
- sizeof (long)) {
if (++ipt
->ipt_oflw
== 0)
sin
= (struct in_addr
*)(cp
+cp
[2]);
if (ipt
->ipt_ptr
+ 8 > ipt
->ipt_len
)
*sin
++ = ((struct sockaddr_in
*)&ifinet
->if_addr
)->sin_addr
;
if (if_ifwithaddr((struct sockaddr
*)&ipaddr
) == 0)
if (ipt
->ipt_ptr
+ 8 > ipt
->ipt_len
)
*(n_time
*)sin
= iptime();
icmp_error(ip
, type
, code
);
* Strip out IP options, at higher
* level protocol in the kernel.
* Second argument is buffer to which options
* will be moved, and return value is their length.
ip_stripoptions(ip
, mopt
)
olen
= (ip
->ip_hl
<<2) - sizeof (struct ip
);
bcopy((caddr_t
)ip
, mtod(mopt
, caddr_t
), (unsigned)olen
);
i
= m
->m_len
- (sizeof (struct ip
) + olen
);
bcopy((caddr_t
)ip
+olen
, (caddr_t
)ip
, (unsigned)i
);
u_char inetctlerrmap
[PRC_NCMDS
] = {
ECONNABORTED
, ECONNABORTED
, 0, 0,
0, 0, EHOSTDOWN
, EHOSTUNREACH
,
ENETUNREACH
, EHOSTUNREACH
, ECONNREFUSED
, ECONNREFUSED
,
int tcp_abort(), udp_abort();
extern struct inpcb tcb
, udb
;
if (cmd
< 0 || cmd
> PRC_NCMDS
)
if (inetctlerrmap
[cmd
] == 0)
in
= &((struct sockaddr_in
*)arg
)->sin_addr
;
else if (cmd
== PRC_HOSTDEAD
|| cmd
== PRC_HOSTUNREACH
)
in
= (struct in_addr
*)arg
;
in
= &((struct icmp
*)arg
)->icmp_ip
.ip_dst
;
/* THIS IS VERY QUESTIONABLE, SHOULD HIT ALL PROTOCOLS */
in_pcbnotify(&tcb
, in
, (int)inetctlerrmap
[cmd
], tcp_abort
);
in_pcbnotify(&udb
, in
, (int)inetctlerrmap
[cmd
], udp_abort
);
* Forward a packet. If some error occurs return the sender
* and icmp packet. Note we can't always generate a meaningful
* icmp message because icmp doesn't have a large enough repetoire
register int error
, type
, code
;
struct mbuf
*mopt
, *mcopy
;
printf("forward: src %x dst %x ttl %x\n", ip
->ip_src
,
/* can't tell difference between net and host */
type
= ICMP_UNREACH
, code
= ICMP_UNREACH_NET
;
if (ip
->ip_ttl
< IPTTLDEC
) {
type
= ICMP_TIMXCEED
, code
= ICMP_TIMXCEED_INTRANS
;
mopt
= m_get(M_DONTWAIT
, MT_DATA
);
* Save at most 64 bytes of the packet in case
* we need to generate an ICMP message to the src.
mcopy
= m_copy(dtom(ip
), 0, imin(ip
->ip_len
, 64));
ip_stripoptions(ip
, mopt
);
error
= ip_output(dtom(ip
), mopt
, (struct route
*)0, IP_FORWARDING
);
ip
= mtod(mcopy
, struct ip
*);
type
= ICMP_UNREACH
, code
= 0; /* need ``undefined'' */
code
= ICMP_UNREACH_NEEDFRAG
;
code
= ICMP_UNREACH_PORT
;
type
= ICMP_SOURCEQUENCH
;
code
= ICMP_UNREACH_HOST
;
icmp_error(ip
, type
, code
);