Subject: Code to set the "default" IP address for outgoing connections
To: None <tech-net@netbsd.org>
From: Michael Graff <explorer@flame.org>
List: tech-net
Date: 09/17/1998 00:19:43
I have a machine with multiple ethernets, and a few ifconfig aliases
on the loopback interface.  I'd like to set a "default" address for
this machine, so unless an address is specified, packets will appear to
come from one of the aliases.

I have code to do this, and it works against -current.  Here are the
diffs.  Comments welcome, flames, well, I suppose that's ok too.

--Michael

Index: sys/netinet/in.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/in.h,v
retrieving revision 1.39
diff -u -u -r1.39 in.h
--- in.h	1998/09/14 21:15:56	1.39
+++ in.h	1998/09/17 07:25:47
@@ -297,7 +297,8 @@
 #define	IPCTL_ANONPORTMAX      11	/* maximum ephemeral port */
 #define	IPCTL_MTUDISCTIMEOUT   12	/* allow path MTU discovery */
 #define	IPCTL_MAXFLOWS         13	/* maximum ip flows allowed */
-#define	IPCTL_MAXID	       14
+#define	IPCTL_SRCADDR	       14	/* default source address */
+#define	IPCTL_MAXID	       15
 
 #define	IPCTL_NAMES { \
 	{ 0, 0 }, \
@@ -314,12 +315,14 @@
 	{ "anonportmax", CTLTYPE_INT }, \
 	{ "mtudisctimeout", CTLTYPE_INT }, \
 	{ "maxflows", CTLTYPE_INT }, \
+	{ "srcaddr", CTLTYPE_STRUCT }, \
 }
 #endif /* !_XOPEN_SOURCE */
 
 
 #ifdef _KERNEL
 extern	struct in_addr zeroin_addr;
+extern	struct in_addr ip_srcaddr;
 
 int	in_broadcast __P((struct in_addr, struct ifnet *));
 int	in_canforward __P((struct in_addr));
Index: sys/netinet/in_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_pcb.c,v
retrieving revision 1.52
diff -u -u -r1.52 in_pcb.c
--- in_pcb.c	1998/08/02 00:35:31	1.52
+++ in_pcb.c	1998/09/17 07:25:51
@@ -96,6 +96,7 @@
 #include <netinet/ip_var.h>
 
 struct	in_addr zeroin_addr;
+struct	in_addr ip_srcaddr;
 
 #define	INPCBHASH_BIND(table, laddr, lport) \
 	&(table)->inpt_bindhashtbl[ \
@@ -290,6 +291,7 @@
 	struct in_ifaddr *ia;
 	struct sockaddr_in *ifaddr = NULL;
 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+	struct sockaddr_in lsin;
 	int error;
 
 	if (nam->m_len != sizeof (*sin))
@@ -332,22 +334,21 @@
 	if (in_nullhost(inp->inp_laddr)) {
 		register struct route *ro;
 
-		ia = (struct in_ifaddr *)0;
+		ia = NULL;
+
 		/* 
 		 * If route is known or can be allocated now,
 		 * our src addr is taken from the i/f, else punt.
 		 */
 		ro = &inp->inp_route;
 		if (ro->ro_rt &&
-		    (!in_hosteq(satosin(&ro->ro_dst)->sin_addr,
-			sin->sin_addr) || 
-		    inp->inp_socket->so_options & SO_DONTROUTE)) {
+		    (!in_hosteq(satosin(&ro->ro_dst)->sin_addr, sin->sin_addr)
+		     || inp->inp_socket->so_options & SO_DONTROUTE)) {
 			RTFREE(ro->ro_rt);
-			ro->ro_rt = (struct rtentry *)0;
+			ro->ro_rt = NULL;
 		}
 		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
-		    (ro->ro_rt == (struct rtentry *)0 ||
-		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+		    (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
 			/* No route yet, so try to acquire one */
 			ro->ro_dst.sa_family = AF_INET;
 			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
@@ -365,39 +366,54 @@
 		if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
 			ia = ifatoia(ro->ro_rt->rt_ifa);
 		if (ia == 0) {
-		    u_int16_t fport = sin->sin_port;
-
-		    sin->sin_port = 0;
-		    ia = ifatoia(ifa_ifwithladdr(sintosa(sin)));
-		    sin->sin_port = fport;
-		    if (ia == 0)
-			/* Find 1st non-loopback AF_INET address */
-			for (ia = in_ifaddr.tqh_first ; ia != NULL ;
-				ia = ia->ia_list.tqe_next)
-			    if (!(ia->ia_ifp->if_flags & IFF_LOOPBACK))
-				break;
-		    if (ia == 0)
-			return (EADDRNOTAVAIL);
+			u_int16_t fport = sin->sin_port;
+			sin->sin_port = 0;
+			ia = ifatoia(ifa_ifwithladdr(sintosa(sin)));
+			sin->sin_port = fport;
+			if (ia == 0)
+				/* Find 1st non-loopback AF_INET address */
+				for (ia = in_ifaddr.tqh_first ; ia != NULL ;
+				     ia = ia->ia_list.tqe_next)
+					if (!(ia->ia_ifp->if_flags & IFF_LOOPBACK))
+						break;
+			if (ia == 0)
+				return (EADDRNOTAVAIL);
 		}
 		/*
-		 * If the destination address is multicast and an outgoing
-		 * interface has been set as a multicast option, use the
-		 * address of that interface as our source address.
+		 * If the destination address is multicast and an
+		 * outgoing interface has been set as a multicast
+		 * option, use the address of that interface as
+		 * our source address.
 		 */
 		if (IN_MULTICAST(sin->sin_addr.s_addr) &&
 		    inp->inp_moptions != NULL) {
 			struct ip_moptions *imo;
 			struct ifnet *ifp;
-
 			imo = inp->inp_moptions;
 			if (imo->imo_multicast_ifp != NULL) {
 				ifp = imo->imo_multicast_ifp;
-				IFP_TO_IA(ifp, ia);		/* XXX */
+				IFP_TO_IA(ifp, ia);	      /* XXX */
 				if (ia == 0)
 					return (EADDRNOTAVAIL);
 			}
 		}
+
 		ifaddr = satosin(&ia->ia_addr);
+
+#if 0
+		printf("src 1 %08x:%d dst %08x:%d, route %08x\n",
+		       (u_int32_t)inp->inp_laddr.s_addr, ntohs(inp->inp_lport),
+		       sin->sin_addr.s_addr, ntohs(sin->sin_port),
+		       ifaddr->sin_addr.s_addr);
+#endif
+
+		if ((in_nullhost(ip_srcaddr) == 0)
+		    && (sin->sin_addr.s_addr != INADDR_LOOPBACK)
+		    && (sin->sin_addr.s_addr != INADDR_ANY)) {
+			lsin.sin_addr = ip_srcaddr;
+			lsin.sin_port = 0;
+			ifaddr = &lsin;
+		}
 	}
 	if (in_pcblookup_connect(inp->inp_table, sin->sin_addr, sin->sin_port,
 	    !in_nullhost(inp->inp_laddr) ? inp->inp_laddr : ifaddr->sin_addr,
@@ -405,8 +421,7 @@
 		return (EADDRINUSE);
 	if (in_nullhost(inp->inp_laddr)) {
 		if (inp->inp_lport == 0) {
-			error = in_pcbbind(inp, (struct mbuf *)0,
-			    (struct proc *)0);
+			error = in_pcbbind(inp, NULL, NULL);
 			/*
 			 * This used to ignore the return value
 			 * completely, but we need to check for
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_input.c,v
retrieving revision 1.70
diff -u -u -r1.70 ip_input.c
--- ip_input.c	1998/09/09 04:57:18	1.70
+++ ip_input.c	1998/09/17 07:25:57
@@ -1416,6 +1416,46 @@
 		return (error);
 	    }
 #endif
+	case IPCTL_SRCADDR: {
+		struct sockaddr_in sin;
+		int s;
+
+		sin.sin_addr = ip_srcaddr;
+
+		error = sysctl_struct(oldp, oldlenp, newp, newlen,
+				      &sin.sin_addr, sizeof(sin.sin_addr));
+
+		if (error)
+			return (error);
+
+		s = splsoftnet();
+
+		/*
+		 * setting this to 0.0.0.0 will make the normal look-for-iface
+		 * logic work again, so allow that.
+		 */
+		if (in_nullhost(sin.sin_addr)) {
+			ip_srcaddr = sin.sin_addr;
+			splx(s);
+			return (0);
+		}
+
+#if 0
+		/*
+		 * search for an interface with this address
+		 */
+		if (ifa_ifwithladdr(sintosa(&sin)) == NULL) {
+			splx(s);
+			return (EADDRNOTAVAIL);
+		}
+#endif
+
+		ip_srcaddr = sin.sin_addr;
+		splx(s);
+
+		return (0);
+	}
+		
 	default:
 		return (EOPNOTSUPP);
 	}
? usr.sbin/sysctl/ktrace.out
Index: usr.sbin/sysctl/sysctl.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/sysctl/sysctl.c,v
retrieving revision 1.16
diff -u -u -r1.16 sysctl.c
--- sysctl.c	1998/03/11 17:44:02	1.16
+++ sysctl.c	1998/09/17 07:26:14
@@ -68,6 +68,8 @@
 #include <netinet/tcp_timer.h>
 #include <netinet/tcp_var.h>
 
+#include <arpa/inet.h>
+
 #include <err.h>
 #include <ctype.h>
 #include <errno.h>
@@ -119,6 +121,7 @@
 #define	CLOCK		0x00000001
 #define	BOOTTIME	0x00000002
 #define	CONSDEV		0x00000004
+#define IP_ADDRESS	0x00000008
 
 int main __P((int, char *[]));
 
@@ -216,6 +219,7 @@
 	void *newval = 0;
 	int intval, newsize = 0;
 	quad_t quadval;
+	struct in_addr ina;
 	size_t size;
 	struct list *lp;
 	int mib[CTL_MAXNAME];
@@ -320,10 +324,13 @@
 	case CTL_NET:
 		if (mib[1] == PF_INET) {
 			len = sysctl_inet(string, &bufp, mib, flags, &type);
+			if (len == 4 && mib[2] == IPPROTO_IP && mib[3] == IPCTL_SRCADDR)
+				special |= IP_ADDRESS;
 			if (len >= 0)
 				break;
 			return;
 		}
+
 		if (flags == 0)
 			return;
 		warnx("Use netstat to view %s information", string);
@@ -368,6 +375,17 @@
 			newval = &quadval;
 			newsize = sizeof quadval;
 			break;
+
+		case CTLTYPE_STRUCT:
+			if (special & IP_ADDRESS) {
+				if (inet_aton(newval, &ina) == 0)
+					errx(1, "Invalid IP address %s",
+					     (char *)newval);
+
+				newval = &ina;
+				newsize = sizeof(ina);
+			}
+			break;
 		}
 	}
 	size = BUFSIZ;
@@ -420,6 +438,13 @@
 			    devname(dev, S_IFCHR));
 		else
 			fprintf(stdout, "0x%x\n", dev);
+		return;
+	}
+	if (special & IP_ADDRESS) {
+		struct in_addr ina2 = *(struct in_addr *)newval;
+
+		fprintf(stdout, "%s = %s\n", string, inet_ntoa(ina2));
+
 		return;
 	}
 	switch (type) {