Subject: Re: ISDN - first TCP connection fails
To: Ingolf Steinbach <ingolf@steinba.ch>
From: Martin Husemann <martin@duskware.de>
List: tech-net
Date: 03/09/2002 14:05:21
--1yeeQ81UyVL57Vl7
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

> Thanks for the patch. Unfortunately, I was not able to
> compile a kernel with it:

Ok, here is a version that compiles. I still can't test it though.

Martin


--1yeeQ81UyVL57Vl7
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=patch

Index: if_sppp.h
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_sppp.h,v
retrieving revision 1.6
diff -c -u -r1.6 if_sppp.h
--- if_sppp.h	2000/05/02 12:43:16	1.6
+++ if_sppp.h	2002/03/09 13:02:40
@@ -51,12 +51,16 @@
 	u_long	opts;		/* IPCP options to send (bitfield) */
 	u_int	flags;
 #define IPCP_HISADDR_SEEN 1	/* have seen his address already */
-#define IPCP_MYADDR_DYN   2	/* my address is dynamically assigned */
-#define IPCP_MYADDR_SEEN  4	/* have seen his address already */
+#define IPCP_MYADDR_SEEN  2	/* have a local address assigned already */
+#define IPCP_MYADDR_DYN   4	/* my address is dynamically assigned */
+#define	IPCP_HISADDR_DYN  8	/* his address is dynamically assigned */
 #ifdef notdef
 #define IPV6CP_MYIFID_DYN   2	/* my ifid is dynamically assigned */
 #endif
 #define IPV6CP_MYIFID_SEEN  4	/* have seen his ifid already */
+	u_int32_t saved_hisaddr;/* if hisaddr (IPv4) is dynamic, save original one here, in network byte order */
+	u_int32_t req_hisaddr;	/* remote address requested */
+	u_int32_t req_myaddr;	/* local address requested */
 };
 
 #define AUTHNAMELEN	32
Index: if_spppsubr.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_spppsubr.c,v
retrieving revision 1.10.4.2
diff -c -u -r1.10.4.2 if_spppsubr.c
--- if_spppsubr.c	2001/07/29 19:51:36	1.10.4.2
+++ if_spppsubr.c	2002/03/09 13:02:41
@@ -393,14 +393,15 @@
 static const char *sppp_state_name(int state);
 static int sppp_params(struct sppp *sp, int cmd, void *data);
 static int sppp_strnlen(u_char *p, int max);
-static void sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst,
-			      u_long *srcmask);
+static void sppp_get_ip_addrs(struct sppp *sp, u_int32_t *src, u_int32_t *dst,
+			      u_int32_t *srcmask);
 static void sppp_keepalive(void *dummy);
 static void sppp_phase_network(struct sppp *sp);
 static void sppp_print_bytes(const u_char *p, u_short len);
 static void sppp_print_string(const char *p, u_short len);
 static void sppp_qflush(struct ifqueue *ifq);
-static void sppp_set_ip_addr(struct sppp *sp, u_long src);
+static void sppp_set_ip_addrs(struct sppp *sp, u_int32_t myaddr, u_int32_t hisaddr);
+static void sppp_clear_ip_addrs(struct sppp *sp);
 #ifdef INET6
 static void sppp_get_ip6_addrs(struct sppp *sp, struct in6_addr *src,
 				struct in6_addr *dst, struct in6_addr *srcmask);
@@ -1152,7 +1153,7 @@
 {
 	STDDCL;
 	struct cisco_packet *h;
-	u_long me, mymask;
+	u_int32_t me, mymask;
 
 	if (m->m_pkthdr.len < CISCO_PACKET_LEN) {
 		if (debug)
@@ -2729,9 +2730,11 @@
 sppp_ipcp_open(struct sppp *sp)
 {
 	STDDCL;
-	u_long myaddr, hisaddr;
+	u_int32_t myaddr, hisaddr;
 
-	sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN|IPCP_MYADDR_SEEN|IPCP_MYADDR_DYN);
+	sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN|IPCP_MYADDR_SEEN|IPCP_MYADDR_DYN|IPCP_HISADDR_DYN);
+	sp->ipcp.req_myaddr = 0;
+	sp->ipcp.req_hisaddr = 0;
 
 	sppp_get_ip_addrs(sp, &myaddr, &hisaddr, 0);
 	/*
@@ -2748,15 +2751,21 @@
 		return;
 	}
 
-	if (myaddr == 0L) {
+	if (myaddr == 0) {
 		/*
 		 * I don't have an assigned address, so i need to
 		 * negotiate my address.
 		 */
 		sp->ipcp.flags |= IPCP_MYADDR_DYN;
 		sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS);
-	} else
-		sp->ipcp.flags |= IPCP_MYADDR_SEEN;
+	}
+	if (hisaddr == 1) {
+		/*
+		 * XXX - remove this hack!
+		 * remote has no valid adress, we need to get one assigned.
+		 */
+		sp->ipcp.flags |= IPCP_HISADDR_DYN;
+	}
 	sppp_open_event(&ipcp, sp);
 }
 
@@ -2764,11 +2773,11 @@
 sppp_ipcp_close(struct sppp *sp)
 {
 	sppp_close_event(&ipcp, sp);
-	if (sp->ipcp.flags & IPCP_MYADDR_DYN)
+	if (sp->ipcp.flags & (IPCP_MYADDR_DYN|IPCP_HISADDR_DYN))
 		/*
-		 * My address was dynamic, clear it again.
+		 * Some address was dynamic, clear it again.
 		 */
-		sppp_set_ip_addr(sp, 0L);
+		sppp_clear_ip_addrs(sp);
 }
 
 static void
@@ -2789,8 +2798,7 @@
 	u_char *buf, *r, *p;
 	struct ifnet *ifp = &sp->pp_if;
 	int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG;
-	u_long hisaddr, desiredaddr;
-	int gotmyaddr = 0;
+	u_int32_t hisaddr, desiredaddr;
 
 	len -= 4;
 	origlen = len;
@@ -2849,7 +2857,10 @@
 		addlog("\n");
 
 	/* pass 2: parse option values */
-	sppp_get_ip_addrs(sp, 0, &hisaddr, 0);
+	if (sp->ipcp.flags & IPCP_HISADDR_SEEN)
+		hisaddr = sp->ipcp.req_hisaddr;	/* we already aggreed on that */
+	else
+		sppp_get_ip_addrs(sp, 0, &hisaddr, 0);	/* user configuration */
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipcp parse opt values: ",
 		       SPP_ARGS(ifp));
@@ -2866,62 +2877,42 @@
 		case IPCP_OPT_ADDRESS:
 			desiredaddr = p[2] << 24 | p[3] << 16 |
 				p[4] << 8 | p[5];
-			if (!(sp->ipcp.flags & IPCP_MYADDR_SEEN) &&
-			        (sp->ipcp.flags & IPCP_MYADDR_DYN)) {
-				/*
-				 * hopefully this is our address !!
-				 */
-			 	if (debug)
-					addlog(" [wantmyaddr %s]",
-						sppp_dotted_quad(desiredaddr));
+			if (desiredaddr == hisaddr ||
+		    	   ((sp->ipcp.flags & IPCP_HISADDR_DYN) && desiredaddr != 0)) {
 				/*
-				 * When doing dynamic address assignment,
-			   	 * we accept his offer.  Otherwise, we
-			    	 * ignore it and thus continue to negotiate
-			     	 * our already existing value.
-		      		 */
-				sppp_set_ip_addr(sp, desiredaddr);
+			 	* Peer's address is same as our value,
+			 	* this is agreeable.  Gonna conf-ack
+			 	* it.
+			 	*/
 				if (debug)
-					addlog(" [agree]");
-				sp->ipcp.flags |= IPCP_MYADDR_SEEN;
-				gotmyaddr++;
+					addlog(" %s [ack]",
+				       		sppp_dotted_quad(hisaddr));
+				/* record that we've seen it already */
+				sp->ipcp.flags |= IPCP_HISADDR_SEEN;
+				sp->ipcp.req_hisaddr = desiredaddr;
+				hisaddr = desiredaddr;
 				continue;
-			} else {
-				if (desiredaddr == hisaddr ||
-			    	(hisaddr == 1 && desiredaddr != 0)) {
-					/*
-				 	* Peer's address is same as our value,
-				 	* this is agreeable.  Gonna conf-ack
-				 	* it.
-				 	*/
-					if (debug)
-						addlog(" %s [ack]",
-					       		sppp_dotted_quad(hisaddr));
-					/* record that we've seen it already */
-					sp->ipcp.flags |= IPCP_HISADDR_SEEN;
-					continue;
-				}
-				/*
-			 	* The address wasn't agreeable.  This is either
-			 	* he sent us 0.0.0.0, asking to assign him an
-			 	* address, or he send us another address not
-			 	* matching our value.  Either case, we gonna
-			 	* conf-nak it with our value.
-			 	*/
-				if (debug) {
-					if (desiredaddr == 0)
-						addlog(" [addr requested]");
-					else
-						addlog(" %s [not agreed]",
-					       		sppp_dotted_quad(desiredaddr));
-				}
-
-				p[2] = hisaddr >> 24;
-				p[3] = hisaddr >> 16;
-				p[4] = hisaddr >> 8;
-				p[5] = hisaddr;
-				break;
 			}
+			/*
+		 	* The address wasn't agreeable.  This is either
+		 	* he sent us 0.0.0.0, asking to assign him an
+		 	* address, or he send us another address not
+		 	* matching our value.  Either case, we gonna
+		 	* conf-nak it with our value.
+		 	*/
+			if (debug) {
+				if (desiredaddr == 0)
+					addlog(" [addr requested]");
+				else
+					addlog(" %s [not agreed]",
+				       		sppp_dotted_quad(desiredaddr));
+			}
+
+			p[2] = hisaddr >> 24;
+			p[3] = hisaddr >> 16;
+			p[4] = hisaddr >> 8;
+			p[5] = hisaddr;
+			break;
 		}
 		/* Add the option to nak'ed list. */
 		bcopy (p, r, p[1]);
@@ -2939,7 +2930,7 @@
 	 * doesn't want to send us his address.  Q: What should we do
 	 * about it?  XXX  A: implement the max-failure counter.
 	 */
-	if (rlen == 0 && !(sp->ipcp.flags & IPCP_HISADDR_SEEN) && !gotmyaddr) {
+	if (rlen == 0 && !(sp->ipcp.flags & IPCP_HISADDR_SEEN)) {
 		buf[0] = IPCP_OPT_ADDRESS;
 		buf[1] = 6;
 		buf[2] = hisaddr >> 24;
@@ -3058,10 +3049,10 @@
 				 * our already existing value.
 				 */
 				if (sp->ipcp.flags & IPCP_MYADDR_DYN) {
-					sppp_set_ip_addr(sp, wantaddr);
 					if (debug)
 						addlog(" [agree]");
 					sp->ipcp.flags |= IPCP_MYADDR_SEEN;
+					sp->ipcp.req_myaddr = wantaddr;
 				}
 			}
 			break;
@@ -3083,7 +3074,14 @@
 static void
 sppp_ipcp_tlu(struct sppp *sp)
 {
-	/* we are up - notify isdn daemon */
+	/* we are up. Set addresses and notify anyone interested */
+	u_int32_t myaddr, hisaddr;
+	sppp_get_ip_addrs(sp, &myaddr, &hisaddr, 0);
+	if ((sp->ipcp.flags & IPCP_MYADDR_DYN) && (sp->ipcp.flags & IPCP_MYADDR_SEEN))
+		myaddr = sp->ipcp.req_myaddr;
+	if ((sp->ipcp.flags & IPCP_HISADDR_DYN) && (sp->ipcp.flags & IPCP_HISADDR_SEEN))
+		hisaddr = sp->ipcp.req_hisaddr;
+	sppp_set_ip_addrs(sp, myaddr, hisaddr);
 	if (sp->pp_con)
 		sp->pp_con(sp);
 }
@@ -3111,7 +3109,7 @@
 sppp_ipcp_scr(struct sppp *sp)
 {
 	char opt[6 /* compression */ + 6 /* address */];
-	u_long ouraddr;
+	u_int32_t ouraddr;
 	int i = 0;
 
 #ifdef notyet
@@ -3126,7 +3124,10 @@
 #endif
 
 	if (sp->ipcp.opts & (1 << IPCP_OPT_ADDRESS)) {
-		sppp_get_ip_addrs(sp, &ouraddr, 0, 0);
+		if (sp->ipcp.flags & IPCP_MYADDR_SEEN)
+			ouraddr = sp->ipcp.req_myaddr;	/* not sure if this can ever happen */
+		else
+			sppp_get_ip_addrs(sp, &ouraddr, 0, 0);
 		opt[i++] = IPCP_OPT_ADDRESS;
 		opt[i++] = 6;
 		opt[i++] = ouraddr >> 24;
@@ -4668,12 +4669,12 @@
  * Get both IP addresses.
  */
 static void
-sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst, u_long *srcmask)
+sppp_get_ip_addrs(struct sppp *sp, u_int32_t *src, u_int32_t *dst, u_int32_t *srcmask)
 {
 	struct ifnet *ifp = &sp->pp_if;
 	struct ifaddr *ifa;
 	struct sockaddr_in *si, *sm;
-	u_long ssrc, ddst;
+	u_int32_t ssrc, ddst;
 
 	sm = NULL;
 	ssrc = ddst = 0L;
@@ -4717,37 +4718,30 @@
 }
 
 /*
- * Set my IP address.  Must be called at splimp.
+ * Set IP addresses.  Must be called at splnet.
+ * If an address is 0, leave it the way it is.
  */
 static void
-sppp_set_ip_addr(struct sppp *sp, u_long src)
+sppp_set_ip_addrs(struct sppp *sp, u_int32_t myaddr, u_int32_t hisaddr)
 {
 	STDDCL;
 	struct ifaddr *ifa;
 	struct sockaddr_in *si;
+	struct sockaddr_in *dest;
 
 	/*
 	 * Pick the first AF_INET address from the list,
 	 * aliases don't make any sense on a p2p link anyway.
 	 */
 
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3
-	for (ifa = ifp->if_addrhead.tqh_first, si = 0;
-	     ifa;
-	     ifa = ifa->ifa_link.tqe_next)
-#elif defined(__NetBSD__) || defined (__OpenBSD__)
 	for (ifa = ifp->if_addrlist.tqh_first, si = 0;
 	     ifa;
 	     ifa = ifa->ifa_list.tqe_next)
-#else
-	for (ifa = ifp->if_addrlist, si = 0;
-	     ifa;
-	     ifa = ifa->ifa_next)
-#endif
 	{
 		if (ifa->ifa_addr->sa_family == AF_INET)
 		{
 			si = (struct sockaddr_in *)ifa->ifa_addr;
+			dest = (struct sockaddr_in *)ifa->ifa_dstaddr;
 			if (si)
 				break;
 		}
@@ -4756,38 +4750,81 @@
 	if (ifa && si)
 	{
 		int error;
-#if __NetBSD_Version__ >= 103080000
 		struct sockaddr_in new_sin = *si;
+		struct sockaddr_in new_dst = *dest;
 
-		new_sin.sin_addr.s_addr = htonl(src);
-		error = in_ifinit(ifp, ifatoia(ifa), &new_sin, 1);
-		if(debug && error)
-		{
-			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: in_ifinit "
-			" failed, error=%d\n", SPP_ARGS(ifp), error);
+		/*
+		 * Scrub old routes now instead of calling in_ifinit with
+		 * scrub=1, because we may change the dstaddr
+		 * before the call to in_ifinit.
+		 */
+		in_ifscrub(ifp, ifatoia(ifa));
+
+		if (myaddr != 0)
+			new_sin.sin_addr.s_addr = htonl(myaddr);
+		if (hisaddr != 0) {
+			new_dst.sin_addr.s_addr = htonl(hisaddr);
+			if (new_dst.sin_addr.s_addr != dest->sin_addr.s_addr) {
+				sp->ipcp.saved_hisaddr = dest->sin_addr.s_addr;
+				*dest = new_dst; /* fix dstaddr in place */
+			}
 		}
-#else
-		/* delete old route */
-		error = rtinit(ifa, (int)RTM_DELETE, RTF_HOST);
+		error = in_ifinit(ifp, ifatoia(ifa), &new_sin, 0);
 		if(debug && error)
 		{
-			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit DEL failed, error=%d\n",
+			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addrs: in_ifinit failed: %d\n",
 		    		SPP_ARGS(ifp), error);
 		}
+	}
+}			
+
+/*
+ * Clear IP addresses.  Must be called at splnet.
+ */
+static void
+sppp_clear_ip_addrs(struct sppp *sp)
+{
+	struct ifnet *ifp = &sp->pp_if;
+	struct ifaddr *ifa;
+	struct sockaddr_in *si;
+	struct sockaddr_in *dest;
+
+	u_int32_t remote;
+	if (sp->ipcp.flags & IPCP_HISADDR_DYN)
+		remote = sp->ipcp.saved_hisaddr;
+	else
+		sppp_get_ip_addrs(sp, 0, &remote, 0);
 
-		/* set new address */
-		si->sin_addr.s_addr = htonl(src);
+	/*
+	 * Pick the first AF_INET address from the list,
+	 * aliases don't make any sense on a p2p link anyway.
+	 */
 
-		/* add new route */
-		error = rtinit(ifa, (int)RTM_ADD, RTF_HOST);		
-		if (debug && error)
+	si = 0;
+	TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+	{
+		if (ifa->ifa_addr->sa_family == AF_INET)
 		{
-			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit ADD failed, error=%d",
-		    		SPP_ARGS(ifp), error);
+			si = (struct sockaddr_in *)ifa->ifa_addr;
+			dest = (struct sockaddr_in *)ifa->ifa_dstaddr;
+			if (si)
+				break;
 		}
-#endif
 	}
-}			
+
+	if (ifa && si)
+	{
+		struct sockaddr_in new_sin = *si;
+
+		in_ifscrub(ifp, ifatoia(ifa));
+		if (sp->ipcp.flags & IPCP_MYADDR_DYN)
+			new_sin.sin_addr.s_addr = 0;
+		if (sp->ipcp.flags & IPCP_HISADDR_DYN)
+			/* replace peer addr in place */
+			dest->sin_addr.s_addr = sp->ipcp.saved_hisaddr;
+		in_ifinit(ifp, ifatoia(ifa), &new_sin, 0);
+	}
+}
 
 #ifdef INET6
 /*

--1yeeQ81UyVL57Vl7--