* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)ip_input.c 8.1 (Berkeley) 6/10/93
#include <netinet/in_systm.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#define IPFORWARDING 1 /* forward IP packets not for us */
#define IPFORWARDING 0 /* don't forward IP packets not for us */
#endif /* IPFORWARDING */
#define IPSENDREDIRECTS 1
int ipforwarding
= IPFORWARDING
;
int ipsendredirects
= IPSENDREDIRECTS
;
int ip_defttl
= IPDEFTTL
;
extern struct domain inetdomain
;
extern struct protosw inetsw
[];
u_char ip_protox
[IPPROTO_MAX
];
int ipqmaxlen
= IFQ_MAXLEN
;
struct in_ifaddr
*in_ifaddr
; /* first inet address */
* We need to save the IP options in case a protocol wants to respond
* to an incoming packet over the same route if the packet got here
* using IP source routing. This allows connection establishment and
* maintenance when the remote end is on a network that is not known
struct in_addr dst
; /* final destination */
char nop
; /* one NOP to align */
char srcopt
[IPOPT_OFFSET
+ 1]; /* OPTVAL, OLEN and OFFSET */
struct in_addr route
[MAX_IPOPTLEN
/sizeof(struct in_addr
)];
static void save_rte
__P((u_char
*, struct in_addr
));
* 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
, SOCK_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_domain
->dom_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
;
i
= (if_index
+ 1) * (if_index
+ 1) * sizeof (u_long
);
ip_ifmatrix
= (u_long
*) malloc(i
, M_RTABLE
, M_WAITOK
);
bzero((char *)ip_ifmatrix
, i
);
struct sockaddr_in ipaddr
= { sizeof(ipaddr
), AF_INET
};
struct route ipforward_rt
;
* Ip input routine. Checksum and byte swap header. If fragmented
* try to reassemble. Process options. Pass to next level.
register struct in_ifaddr
*ia
;
* Get next datagram off input queue and get IP header
if ((m
->m_flags
& M_PKTHDR
) == 0)
* If no IP addresses have been set yet but the interfaces
* are receiving, can't do anything with incoming packets yet.
if (m
->m_len
< sizeof (struct ip
) &&
(m
= m_pullup(m
, sizeof (struct ip
))) == 0) {
ip
= mtod(m
, struct ip
*);
if (ip
->ip_v
!= IPVERSION
) {
if (hlen
< sizeof(struct ip
)) { /* minimum header length */
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.
* 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.
if (m
->m_pkthdr
.len
< ip
->ip_len
) {
if (m
->m_pkthdr
.len
> ip
->ip_len
) {
if (m
->m_len
== m
->m_pkthdr
.len
) {
m
->m_pkthdr
.len
= ip
->ip_len
;
m_adj(m
, ip
->ip_len
- m
->m_pkthdr
.len
);
* Process options and, if not destined for us,
* ship it on. ip_dooptions returns 1 when an
* error was detected (causing an icmp message
* to be sent and the original packet to be freed).
ip_nhops
= 0; /* for source routed packets */
if (hlen
> sizeof (struct ip
) && ip_dooptions(m
))
* Check our list of addresses, to see if the packet is for us.
for (ia
= in_ifaddr
; ia
; ia
= ia
->ia_next
) {
#define satosin(sa) ((struct sockaddr_in *)(sa))
if (IA_SIN(ia
)->sin_addr
.s_addr
== ip
->ip_dst
.s_addr
)
#ifdef DIRECTED_BROADCAST
ia
->ia_ifp
== m
->m_pkthdr
.rcvif
&&
(ia
->ia_ifp
->if_flags
& IFF_BROADCAST
)) {
if (satosin(&ia
->ia_broadaddr
)->sin_addr
.s_addr
==
if (ip
->ip_dst
.s_addr
== ia
->ia_netbroadcast
.s_addr
)
* Look for all-0's host part (old broadcast addr),
* either for subnet or net.
t
= ntohl(ip
->ip_dst
.s_addr
);
if (IN_MULTICAST(ntohl(ip
->ip_dst
.s_addr
))) {
extern struct socket
*ip_mrouter
;
* If we are acting as a multicast router, all
* incoming multicast packets are passed to the
* kernel-level multicast forwarding function.
* The packet is returned (relatively) intact; if
* ip_mforward() returns a non-zero value, the packet
* must be discarded, else it may be accepted below.
* (The IP ident field is put in the same byte order
* as expected when ip_mforward() is called from
ip
->ip_id
= htons(ip
->ip_id
);
if (ip_mforward(m
, m
->m_pkthdr
.rcvif
) != 0) {
ipstat
.ips_cantforward
++;
ip
->ip_id
= ntohs(ip
->ip_id
);
* The process-level routing demon needs to receive
* all multicast IGMP packets, whether or not this
* host belongs to their destination groups.
if (ip
->ip_p
== IPPROTO_IGMP
)
* See if we belong to the destination multicast group on the
IN_LOOKUP_MULTI(ip
->ip_dst
, m
->m_pkthdr
.rcvif
, inm
);
ipstat
.ips_cantforward
++;
if (ip
->ip_dst
.s_addr
== (u_long
)INADDR_BROADCAST
)
if (ip
->ip_dst
.s_addr
== INADDR_ANY
)
* Not for us; forward if possible and desirable.
ipstat
.ips_cantforward
++;
* If offset or IP_MF are set, must reassemble.
* Otherwise, nothing need be done.
* (We could look in the reassembly queue to see
* if the packet was previously fragmented,
* but it's not worth the time; just let them time out.)
if (ip
->ip_off
&~ IP_DF
) {
if (m
->m_flags
& M_EXT
) { /* XXX */
if ((m
= m_pullup(m
, sizeof (struct ip
))) == 0) {
ip
= mtod(m
, struct ip
*);
* 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
&= ~1;
((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
& 1 || ip
->ip_off
) {
ip
= ip_reass((struct ipasfrag
*)ip
, fp
);
ipstat
.ips_reassembled
++;
* Switch out to protocol's input routine.
(*inetsw
[ip_protox
[ip
->ip_p
]].pr_input
)(m
, hlen
);
* 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_DONTWAIT
, 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
& 1)
* 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
+= (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 */
for (t
= m
; m
; m
= m
->m_next
)
return ((struct ip
*)ip
);
ipstat
.ips_fragdropped
++;
* 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) {
ipstat
.ips_fragtimeout
++;
* Drain off all datagram fragments.
while (ipq
.next
!= &ipq
) {
ipstat
.ips_fragdropped
++;
* 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.
register struct ip
*ip
= mtod(m
, struct ip
*);
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
;
cnt
= (ip
->ip_hl
<< 2) - sizeof (struct ip
);
for (; cnt
> 0; cnt
-= optlen
, cp
+= optlen
) {
if (optlen
<= 0 || optlen
> cnt
) {
code
= &cp
[IPOPT_OLEN
] - (u_char
*)ip
;
* 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.
if ((off
= cp
[IPOPT_OFFSET
]) < IPOPT_MINOFF
) {
code
= &cp
[IPOPT_OFFSET
] - (u_char
*)ip
;
ipaddr
.sin_addr
= ip
->ip_dst
;
ia
= (struct in_ifaddr
*)
ifa_ifwithaddr((struct sockaddr
*)&ipaddr
);
code
= ICMP_UNREACH_SRCFAIL
;
* Loose routing, and not at next destination
* yet; nothing to do except forward.
if (off
> optlen
- sizeof(struct in_addr
)) {
* End of source route. Should be for us.
save_rte(cp
, ip
->ip_src
);
* locate outgoing interface
bcopy((caddr_t
)(cp
+ off
), (caddr_t
)&ipaddr
.sin_addr
,
sizeof(ipaddr
.sin_addr
));
#define INA struct in_ifaddr *
#define SA struct sockaddr *
if ((ia
= (INA
)ifa_ifwithdstaddr((SA
)&ipaddr
)) == 0)
ia
= (INA
)ifa_ifwithnet((SA
)&ipaddr
);
ia
= ip_rtaddr(ipaddr
.sin_addr
);
code
= ICMP_UNREACH_SRCFAIL
;
ip
->ip_dst
= ipaddr
.sin_addr
;
bcopy((caddr_t
)&(IA_SIN(ia
)->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(ntohl(ip
->ip_dst
.s_addr
));
if ((off
= cp
[IPOPT_OFFSET
]) < IPOPT_MINOFF
) {
code
= &cp
[IPOPT_OFFSET
] - (u_char
*)ip
;
* If no space remains, ignore.
if (off
> optlen
- sizeof(struct in_addr
))
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) {
code
= ICMP_UNREACH_HOST
;
bcopy((caddr_t
)&(IA_SIN(ia
)->sin_addr
),
(caddr_t
)(cp
+ off
), sizeof(struct in_addr
));
cp
[IPOPT_OFFSET
] += sizeof(struct in_addr
);
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
+ ipt
->ipt_ptr
- 1);
if (ipt
->ipt_ptr
+ sizeof(n_time
) +
sizeof(struct in_addr
) > ipt
->ipt_len
)
ia
= (INA
)ifaof_ifpforaddr((SA
)&ipaddr
,
bcopy((caddr_t
)&IA_SIN(ia
)->sin_addr
,
(caddr_t
)sin
, sizeof(struct in_addr
));
ipt
->ipt_ptr
+= sizeof(struct in_addr
);
if (ipt
->ipt_ptr
+ sizeof(n_time
) +
sizeof(struct in_addr
) > ipt
->ipt_len
)
bcopy((caddr_t
)sin
, (caddr_t
)&ipaddr
.sin_addr
,
if (ifa_ifwithaddr((SA
)&ipaddr
) == 0)
ipt
->ipt_ptr
+= sizeof(struct in_addr
);
bcopy((caddr_t
)&ntime
, (caddr_t
)cp
+ ipt
->ipt_ptr
- 1,
ipt
->ipt_ptr
+= sizeof(n_time
);
ip
->ip_len
-= ip
->ip_hl
<< 2; /* XXX icmp_error adds in hdr length */
icmp_error(m
, type
, code
, 0, 0);
* Given address of next destination (final or next hop),
* return internet address info of interface to be used to get there.
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
);
sin
->sin_family
= AF_INET
;
sin
->sin_len
= sizeof(*sin
);
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.
olen
= option
[IPOPT_OLEN
];
printf("save_rte: olen %d\n", olen
);
if (olen
> sizeof(ip_srcrt
) - (1 + sizeof(dst
)))
bcopy((caddr_t
)option
, (caddr_t
)ip_srcrt
.srcopt
, olen
);
ip_nhops
= (olen
- IPOPT_OFFSET
- 1) / sizeof(struct in_addr
);
* Retrieve incoming source route for use in replies,
* in the same form used by setsockopt.
* The first hop is placed before the options, will be removed later.
register struct in_addr
*p
, *q
;
return ((struct mbuf
*)0);
m
= m_get(M_DONTWAIT
, MT_SOOPTS
);
return ((struct mbuf
*)0);
#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
m
->m_len
= ip_nhops
* sizeof(struct in_addr
) + sizeof(struct in_addr
) +
printf("ip_srcroute: nhops %d mlen %d", ip_nhops
, m
->m_len
);
* First save first hop for return route
p
= &ip_srcrt
.route
[ip_nhops
- 1];
*(mtod(m
, struct in_addr
*)) = *p
--;
printf(" hops %lx", ntohl(mtod(m
, struct in_addr
*)->s_addr
));
* Copy option fields and padding (nop) to mbuf.
ip_srcrt
.nop
= IPOPT_NOP
;
ip_srcrt
.srcopt
[IPOPT_OFFSET
] = IPOPT_MINOFF
;
bcopy((caddr_t
)&ip_srcrt
.nop
,
mtod(m
, caddr_t
) + sizeof(struct in_addr
), OPTSIZ
);
q
= (struct in_addr
*)(mtod(m
, caddr_t
) +
sizeof(struct in_addr
) + OPTSIZ
);
* Record return path as an IP source route,
* reversing the path (pointers are now aligned).
while (p
>= ip_srcrt
.route
) {
printf(" %lx", ntohl(q
->s_addr
));
* Last hop goes to final destination.
printf(" %lx\n", ntohl(q
->s_addr
));
* 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.
* XXX should be deleted; last arg currently ignored.
struct ip
*ip
= mtod(m
, struct ip
*);
olen
= (ip
->ip_hl
<<2) - sizeof (struct ip
);
opts
= (caddr_t
)(ip
+ 1);
i
= m
->m_len
- (sizeof (struct ip
) + olen
);
bcopy(opts
+ olen
, opts
, (unsigned)i
);
if (m
->m_flags
& M_PKTHDR
)
ip
->ip_hl
= sizeof(struct ip
) >> 2;
u_char inetctlerrmap
[PRC_NCMDS
] = {
0, EMSGSIZE
, EHOSTDOWN
, EHOSTUNREACH
,
EHOSTUNREACH
, EHOSTUNREACH
, ECONNREFUSED
, ECONNREFUSED
,
EMSGSIZE
, EHOSTUNREACH
, 0, 0,
* Forward a packet. If some error occurs return the sender
* an icmp packet. Note we can't always generate a meaningful
* icmp message because icmp doesn't have a large enough repertoire
* If not forwarding, just drop the packet. This could be confusing
* if ipforwarding was zero but some routing protocol was advancing
* us as a gateway to somewhere. However, we must let the routing
* protocol deal with that.
* The srcrt parameter indicates whether the packet is being forwarded
register struct ip
*ip
= mtod(m
, struct ip
*);
register struct sockaddr_in
*sin
;
register struct rtentry
*rt
;
int error
, type
= 0, code
;
printf("forward: src %x dst %x ttl %x\n", ip
->ip_src
,
if (m
->m_flags
& M_BCAST
|| in_canforward(ip
->ip_dst
) == 0) {
ipstat
.ips_cantforward
++;
if (ip
->ip_ttl
<= IPTTLDEC
) {
icmp_error(m
, ICMP_TIMXCEED
, ICMP_TIMXCEED_INTRANS
, dest
, 0);
sin
= (struct sockaddr_in
*)&ipforward_rt
.ro_dst
;
if ((rt
= ipforward_rt
.ro_rt
) == 0 ||
ip
->ip_dst
.s_addr
!= sin
->sin_addr
.s_addr
) {
if (ipforward_rt
.ro_rt
) {
RTFREE(ipforward_rt
.ro_rt
);
sin
->sin_family
= AF_INET
;
sin
->sin_len
= sizeof(*sin
);
sin
->sin_addr
= ip
->ip_dst
;
if (ipforward_rt
.ro_rt
== 0) {
icmp_error(m
, ICMP_UNREACH
, ICMP_UNREACH_HOST
, dest
, 0);
* Save at most 64 bytes of the packet in case
* we need to generate an ICMP message to the src.
mcopy
= m_copy(m
, 0, imin((int)ip
->ip_len
, 64));
ip_ifmatrix
[rt
->rt_ifp
->if_index
+
if_index
* m
->m_pkthdr
.rcvif
->if_index
]++;
* If forwarding packet using same interface that it came in on,
* perhaps should send a redirect to sender to shortcut a hop.
* Only send redirect if source is sending directly to us,
* and if packet was not source routed (or has any options).
* Also, don't send redirect if forwarding using a default route
* or a route modified by a redirect.
#define satosin(sa) ((struct sockaddr_in *)(sa))
if (rt
->rt_ifp
== m
->m_pkthdr
.rcvif
&&
(rt
->rt_flags
& (RTF_DYNAMIC
|RTF_MODIFIED
)) == 0 &&
satosin(rt_key(rt
))->sin_addr
.s_addr
!= 0 &&
ipsendredirects
&& !srcrt
) {
#define RTA(rt) ((struct in_ifaddr *)(rt->rt_ifa))
u_long src
= ntohl(ip
->ip_src
.s_addr
);
u_long dst
= ntohl(ip
->ip_dst
.s_addr
);
(src
& RTA(rt
)->ia_subnetmask
) == RTA(rt
)->ia_subnet
) {
if (rt
->rt_flags
& RTF_GATEWAY
)
dest
= satosin(rt
->rt_gateway
)->sin_addr
.s_addr
;
dest
= ip
->ip_dst
.s_addr
;
/* Router requirements says to only send host redirects */
code
= ICMP_REDIRECT_HOST
;
printf("redirect (%d) to %lx\n", code
, (u_long
)dest
);
error
= ip_output(m
, (struct mbuf
*)0, &ipforward_rt
, IP_FORWARDING
#ifdef DIRECTED_BROADCAST
ipstat
.ips_cantforward
++;
ipstat
.ips_redirectsent
++;
case 0: /* forwarded, but need redirect */
/* type, code set above */
case ENETUNREACH
: /* shouldn't happen, checked above */
code
= ICMP_UNREACH_HOST
;
code
= ICMP_UNREACH_NEEDFRAG
;
destifp
= ipforward_rt
.ro_rt
->rt_ifp
;
type
= ICMP_SOURCEQUENCH
;
icmp_error(mcopy
, type
, code
, dest
, destifp
);
ip_sysctl(name
, namelen
, oldp
, oldlenp
, newp
, newlen
)
/* all sysctl names at this level are terminal */
return (sysctl_int(oldp
, oldlenp
, newp
, newlen
, &ipforwarding
));
case IPCTL_SENDREDIRECTS
:
return (sysctl_int(oldp
, oldlenp
, newp
, newlen
,
return (sysctl_int(oldp
, oldlenp
, newp
, newlen
, &ip_defttl
));
return (sysctl_int(oldp
, oldlenp
, newp
, newlen
, &ip_mtu
));