Hide the interface name in the sin_zero section of the sockaddr_in

passed to the user process for incoming packets. When the sockaddr_in
is passed back to the divert socket later, use thi sas the primary
interface lookup and only revert to the IP address when the name fails.
This solves a long standing bug with divert sockets:
When two interfaces had the same address (P2P for example) the interface
"assigned" to the reinjected packet was sometimes incorect.
Probably we should define a "sockaddr_div" to officially hold this
extended information in teh same manner as sockaddr_dl.
This commit is contained in:
julian 1998-05-25 08:44:31 +00:00
parent b8fe908514
commit 9fc01fa211
2 changed files with 61 additions and 11 deletions

View File

@ -1,4 +1,4 @@
.\" $Id: divert.4,v 1.9 1997/11/08 01:02:08 brian Exp $
.\" $Id: divert.4,v 1.10 1998/03/12 07:30:16 charnier Exp $
.\"
.Dd June 18, 1996
.Dt DIVERT 4
@ -47,7 +47,9 @@ the divert port and the IP address set to the (first) address of
the interface on which the packet was received (if the packet
was incoming) or
.Dv INADDR_ANY
(if the packet was outgoing).
(if the packet was outgoing). In the case of an incoming packet the interface
name will also be placed in the 8 bytes following the address,
(assuming it fits).
.Sh WRITING PACKETS
Writing to a divert socket is similar to writing to a raw IP socket;
the packet is injected ``as is'' into the normal kernel IP packet
@ -66,7 +68,13 @@ for a non-local address. Otherwise, the packet is assumed to be
incoming and full packet routing is done.
.Pp
In the latter case, the
IP address specified must match the address of some local interface.
IP address specified must match the address of some local interface,
or an interface name
must be found after the IP address. If an interface name is found,
that interface will be used and the value of the IP address will be
ignored (other than the fact that it is not
.Dv INADDR_ANY
).
This is to indicate on which interface the packet ``arrived.''
.Pp
Normally, packets read as incoming should be written as incoming;

View File

@ -30,7 +30,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ip_divert.c,v 1.23 1998/05/15 20:11:33 wollman Exp $
* $Id: ip_divert.c,v 1.24 1998/05/25 07:41:23 julian Exp $
*/
#include "opt_inet.h"
@ -160,6 +160,7 @@ div_input(struct mbuf *m, int hlen)
divsrc.sin_addr.s_addr = 0;
if (hlen) {
struct ifaddr *ifa;
char name[32];
#ifdef DIAGNOSTIC
/* Sanity check */
@ -170,7 +171,7 @@ div_input(struct mbuf *m, int hlen)
/* More fields affected by ip_input() */
HTONS(ip->ip_id);
/* Find IP address for recieve interface */
/* Find IP address for receive interface */
for (ifa = m->m_pkthdr.rcvif->if_addrhead.tqh_first;
ifa != NULL; ifa = ifa->ifa_link.tqe_next) {
if (ifa->ifa_addr == NULL)
@ -181,6 +182,27 @@ div_input(struct mbuf *m, int hlen)
((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
break;
}
/*
* Hide the actual interface name in there in the
* sin_zero array. XXX This needs to be moved to a
* different sockaddr type for divert, e.g.
* sockaddr_div with multiple fields like
* sockaddr_dl. Presently we have only 7 bytes
* but that will do for now as most interfaces
* are 4 or less + 2 or less bytes for unit.
* There is probably a faster way of doing this,
* possibly taking it from the sockaddr_dl on the iface.
* This solves the problem of a P2P link and a LAN interface
* having the same address, which can result in the wrong
* interface being assigned to the packet when fed back
* into the divert socket. Theoretically if the daemon saves
* and re-uses the sockaddr_in as suggested in the man pages,
* this iface name will come along for the ride.
* (see div_output for the other half of this.)
*/
sprintf(name, "%s%d",
m->m_pkthdr.rcvif->if_name, m->m_pkthdr.rcvif->if_unit);
strncpy(divsrc.sin_zero, name, 7);
}
/* Put packet on socket queue, if any */
@ -254,15 +276,35 @@ div_output(so, m, addr, control)
(so->so_options & SO_DONTROUTE) |
IP_ALLOWBROADCAST | IP_RAWOUTPUT, inp->inp_moptions);
} else {
struct ifaddr *ifa;
struct ifnet *ifp = NULL;
struct ifaddr *ifa;
int len = 0;
char *c = sin->sin_zero;
/* Find receive interface with the given IP address */
sin->sin_port = 0;
if ((ifa = ifa_ifwithaddr((struct sockaddr *) sin)) == 0) {
error = EADDRNOTAVAIL;
goto cantsend;
/*
* Find receive interface with the given name or IP address.
* The name is user supplied data so don't trust it's size or
* that it is zero terminated. The name has priority.
* We are presently assuming that the sockaddr_in
* has not been replaced by a sockaddr_div, so we limit it
* to 16 bytes in total. the name is stuffed (if it exists)
* in the sin_zero[] field.
*/
while (*c++ && (len++ < sizeof(sin->sin_zero)));
if ((len > 0) && (len < sizeof(sin->sin_zero)))
ifp = ifunit(sin->sin_zero);
/* If no luck with the name. check by IP address. */
if (ifp) {
m->m_pkthdr.rcvif = ifp;
} else {
if (!(ifa = ifa_ifwithaddr((struct sockaddr *) sin))) {
error = EADDRNOTAVAIL;
goto cantsend;
}
m->m_pkthdr.rcvif = ifa->ifa_ifp;
}
m->m_pkthdr.rcvif = ifa->ifa_ifp;
/* Send packet to input processing */
ip_input(m);