Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > e62f51b3c9c4797a380d8e815dd2df0a > files > 23

dhcpv6-0.10-33.el5.src.rpm

Index: ReadMe
===================================================================
RCS file: /opt/data/qualcomm/cvsroot/dhcp6/ReadMe,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ReadMe	21 Nov 2003 16:36:36 -0000	1.1
+++ ReadMe	5 Dec 2003 21:43:56 -0000	1.2
@@ -18,7 +18,8 @@
 
 	This implementation supports IPv6 address assignment to the clients. 
 It also has the support for prefix delegation, DNS server updates but those 
-features are not validated yet. 
+features are not validated yet. The server also supports messages sent through
+relay agents, both inbound and outbound.
 
 	The Linux implementation is based upon KAME's DHCPv6 implementation 
 on BSD which lacked the support for dynamic address assignment feature, now 
@@ -34,7 +35,8 @@
       3. Client IPv6 address assignment and temporary IPv6 address assignment support 
 	 on the same link. 
       4. Supported Options: Rapid commit, Server Preference, Information Request, Unicast,
-	 Elapsed Time, ClientID, ServerID , IA_NA , IA_TA , IA_ADDR , IA_PD, Status support. 
+	 Elapsed Time, ClientID, ServerID , IA_NA , IA_TA , IA_ADDR , IA_PD, Status,
+	 Relay Message, Interface Identifier. 
       5. Solicit/Request/Advertise/Reply/Infomation-request messages/Renew/Rebind
       	 /Release/Confirm/ messages support for IPv6 address binding. 
       8. Client configuration file support for IPv6 address assignment. 
@@ -47,13 +49,13 @@
 B. Support available but not validated yet 
 
       1. Request option support 
+      2. Relay-Forw/Relay-Repl messages and Relay Message Option
 
 C. ToDo List: 
 
       1. Authentication/User class/Vendor class/Interface-ID option 
          support 
-      2. Relay agent support. 
-      3. Reconfig/Relay messages support. 
+      2. Reconfig message support. 
 
 D. INSTALLATTION:
 
Index: common.c
===================================================================
RCS file: /opt/data/qualcomm/cvsroot/dhcp6/common.c,v
retrieving revision 1.1
retrieving revision 1.4
diff -u -r1.1 -r1.4
--- common.c	21 Nov 2003 16:36:36 -0000	1.1
+++ common.c	5 Dec 2003 22:29:42 -0000	1.4
@@ -215,6 +215,25 @@
 	return;
 }
 
+void
+relayfree(head)
+	struct relay_list *head;
+{
+	struct relay_listval *v;
+
+	while ((v = TAILQ_FIRST(head)) != NULL) {
+		TAILQ_REMOVE(head, v, link);
+		if (v->intf_id != NULL) {
+			if (v->intf_id->intf_id != NULL) 
+				free(v->intf_id->intf_id);
+			free (v->intf_id);
+		}
+		free(v);
+	}
+
+	return;
+}
+
 int
 dhcp6_count_list(head)
 	struct dhcp6_list *head;
@@ -837,6 +856,7 @@
 	TAILQ_INIT(&optinfo->reqopt_list);
 	TAILQ_INIT(&optinfo->stcode_list);
 	TAILQ_INIT(&optinfo->dns_list.addrlist);
+	TAILQ_INIT(&optinfo->relay_list);
 	optinfo->dns_list.domainlist = NULL;
 }
 
@@ -852,6 +872,7 @@
 	dhcp6_clear_list(&optinfo->reqopt_list);
 	dhcp6_clear_list(&optinfo->stcode_list);
 	dhcp6_clear_list(&optinfo->dns_list.addrlist);
+	relayfree(&optinfo->relay_list);
 	if (dhcp6_mode == DHCP6_MODE_CLIENT) {
 		for (dlist = optinfo->dns_list.domainlist; dlist; dlist = dlist_next) {
 			dlist_next = dlist->next;
@@ -1833,6 +1854,10 @@
 		return "rapid commit";
 	case DH6OPT_DNS_RESOLVERS:
 		return "DNS_RESOLVERS";
+	case DH6OPT_RELAY_MSG:
+		return "relay message";
+	case DH6OPT_INTERFACE_ID:
+		return "interface identifier";
 	default:
 		sprintf(genstr, "opt_%d", type);
 		return (genstr);
Index: common.h
===================================================================
RCS file: /opt/data/qualcomm/cvsroot/dhcp6/common.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- common.h	21 Nov 2003 16:36:36 -0000	1.1
+++ common.h	26 Nov 2003 01:17:45 -0000	1.2
@@ -98,6 +98,7 @@
 extern int duidcpy __P((struct duid *, const struct duid *));
 extern int duidcmp __P((const struct duid *, const struct duid *));
 extern void duidfree __P((struct duid *));
+extern void relayfree __P((struct relay_list *));
 extern void ifinit __P((const char *));
 extern int configure_duid __P((const char *, struct duid *));
 extern struct dhcp6_if *find_ifconfbyname __P((const char *));
Index: dhcp6.h
===================================================================
RCS file: /opt/data/qualcomm/cvsroot/dhcp6/dhcp6.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- dhcp6.h	21 Nov 2003 16:36:36 -0000	1.1
+++ dhcp6.h	26 Nov 2003 01:17:45 -0000	1.2
@@ -124,6 +124,11 @@
 	char *duid_id;		/* variable length ID value (must be opaque) */
 };
 
+struct intf_id {
+	u_int16_t intf_len;	/* length */
+	char *intf_id;		/* variable length ID value (must be opaque) */
+};
+
 /* iaid info for the IA_NA */
 struct dhcp6_iaid_info {
 	u_int32_t iaid;
@@ -186,6 +191,28 @@
 	struct domain_list *domainlist;
 };
 
+/* DHCP6 relay agent base packet format */
+struct dhcp6_relay {
+	u_int8_t dh6_msg_type;
+	u_int8_t dh6_hop_count;
+	struct in6_addr link_addr;
+	struct in6_addr peer_addr;
+	/* options follow */
+} __attribute__ ((__packed__));
+
+
+struct relay_listval {
+	TAILQ_ENTRY(relay_listval) link;
+	
+	struct dhcp6_relay relay;
+	struct intf_id *intf_id;
+
+	/* pointer to the Relay Message option in the RELAY-REPL */
+	struct dhcp6opt *option;
+};
+
+TAILQ_HEAD (relay_list, relay_listval);
+
 struct dhcp6_optinfo {
 	struct duid clientID;	/* DUID */
 	struct duid serverID;	/* DUID */
@@ -199,6 +226,8 @@
 	struct dhcp6_list reqopt_list; /*  options in option request */
 	struct dhcp6_list stcode_list; /* status code */
 	struct dns_list dns_list; /* DNS server list */
+	struct relay_list relay_list; /* list of the relays the message passed
+	                                 through on its way to the server */
 };
 
 /* DHCP6 base packet format */
@@ -224,8 +253,7 @@
 #  define DH6OPT_PREF_UNDEF 0 
 #  define DH6OPT_PREF_MAX 255
 #define DH6OPT_ELAPSED_TIME 8
-#define DH6OPT_CLIENT_MSG 9
-#define DH6OPT_SERVER_MSG 10
+#define DH6OPT_RELAY_MSG 9
 #define DH6OPT_AUTH 11
 #define DH6OPT_UNICAST 12
 #define DH6OPT_STATUS_CODE 13
Index: dhcp6s.c
===================================================================
RCS file: /opt/data/qualcomm/cvsroot/dhcp6/dhcp6s.c,v
retrieving revision 1.1
retrieving revision 1.5
diff -u -r1.1 -r1.5
--- dhcp6s.c	21 Nov 2003 16:36:36 -0000	1.1
+++ dhcp6s.c	8 Dec 2003 16:43:37 -0000	1.5
@@ -99,6 +99,7 @@
 char server6_lease_temp[100];
 
 static const struct sockaddr_in6 *sa6_any_downstream;
+static u_int16_t upstream_port;
 static struct msghdr rmh;
 static char rdatabuf[BUFSIZ];
 static int rmsgctllen;
@@ -132,6 +133,15 @@
 			     struct sockaddr *, int,
 			     struct dhcp6_optinfo *));
 static struct dhcp6_timer *check_lease_file_timo __P((void *arg));
+static struct dhcp6 *dhcp6_parse_relay __P((struct dhcp6_relay *,
+                                            struct dhcp6_relay *,
+                                            struct dhcp6_optinfo *,
+                                            struct in6_addr *));
+static int dhcp6_set_relay __P((struct dhcp6_relay *,
+                                struct dhcp6_relay *,
+                                struct dhcp6_optinfo *));
+static void dhcp6_set_relay_option_len __P((struct dhcp6_optinfo *,
+                                           int len));
 extern struct link_decl *dhcp6_allocate_link __P((struct dhcp6_if *, struct rootgroup *, 
 			struct in6_addr *));
 extern struct host_decl *dhcp6_allocate_host __P((struct dhcp6_if *, struct rootgroup *, 
@@ -296,6 +306,7 @@
 			FNAME, strerror(errno));
 		exit(1);
 	}
+	upstream_port = ((struct sockaddr_in6 *) res->ai_addr)->sin6_port;
 	freeaddrinfo(res);
 
 	/* initiallize outbound interface */
@@ -491,6 +502,7 @@
 	struct dhcp6_if *ifp;
 	struct dhcp6 *dh6;
 	struct dhcp6_optinfo optinfo;
+	struct in6_addr relay;  /* the address of the first relay, if any */
 	memset(&iov, 0, sizeof(iov));
 	memset(&mhdr, 0, sizeof(mhdr));
 
@@ -539,10 +551,32 @@
 	    dhcp6msgstr(dh6->dh6_msgtype),
 	    addr2str((struct sockaddr *)&from));
 
+	dhcp6_init_options(&optinfo);
+
+	/*
+	 * If this is a relayed message, parse all of the relay data, storing
+	 * the link addresses, peer addresses, and interface identifiers for
+	 * later use. Get a pointer to the original client message.
+	 */
+	if (dh6->dh6_msgtype == DH6_RELAY_FORW) {
+		dh6 = dhcp6_parse_relay((struct dhcp6_relay *) dh6, 
+		                        (struct dhcp6_relay *) (rdatabuf + len), 
+		                        &optinfo, &relay);
+
+		/*
+		 * NULL means there was an error in the relay format or no
+		 * client message was found.
+		 */
+		if (dh6 == NULL) {
+			dprintf(LOG_INFO, "%s" "failed to parse relay fields "
+			                       "or could not find client message", FNAME);
+			return -1;
+		}
+	}
+ 
 	/*
 	 * parse and validate options in the request
 	 */
-	dhcp6_init_options(&optinfo);
 	if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1),
 	    (struct dhcp6opt *)(rdatabuf + len), &optinfo) < 0) {
 		dprintf(LOG_INFO, "%s" "failed to parse options", FNAME);
@@ -554,7 +588,17 @@
 	 * now assume client is on the same link as server
 	 * if the subnet couldn't be found return status code NotOnLink to client
 	 */
-	subnet = dhcp6_allocate_link(ifp, globalgroup, NULL);
+	/*
+	 * If the relay list is empty, then this is a message received directly
+	 * from the client, so client is on the same link as the server. 
+	 * Otherwise, allocate the client an address based on the first relay
+	 * that forwarded the message.
+	 */
+	if (TAILQ_EMPTY(&optinfo.relay_list))
+		subnet = dhcp6_allocate_link(ifp, globalgroup, NULL);
+	else
+		subnet = dhcp6_allocate_link(ifp, globalgroup, &relay);
+
 	if (!(DH6_VALID_MESSAGE(dh6->dh6_msgtype)))
 		dprintf(LOG_INFO, "%s" "unknown or unsupported msgtype %s",
 		    FNAME, dhcp6msgstr(dh6->dh6_msgtype));
@@ -680,15 +724,28 @@
 	case DH6_REQUEST:
 	case DH6_RENEW:
 	case DH6_DECLINE:
-		if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) {
+		/* 
+		 * If the message was relayed, then do not check whether the message
+		 * came in via unicast or multicast, since the relay may be configured
+		 * to send messages via unicast.
+		 */
+		if (TAILQ_EMPTY(&optinfo->relay_list) && 
+		    !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) {
 			if (!(roptinfo.flags & DHCIFF_UNICAST)) {
 				num = DH6OPT_STCODE_USEMULTICAST;
 				goto send;
 			} else
 				break;
-		} 
+		}
+		break;
 	default:
-		if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) {
+		/* 
+		 * If the message was relayed, then do not check whether the message
+		 * came in via unicast or multicast, since the relay may be configured
+		 * to send messages via unicast.
+		 */
+		if (TAILQ_EMPTY(&optinfo->relay_list) && 
+		    !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) {
 			num = DH6OPT_STCODE_USEMULTICAST;
 			goto send;
 		}
@@ -925,7 +982,7 @@
 {
 	char replybuf[BUFSIZ];
 	struct sockaddr_in6 dst;
-	int len, optlen;
+	int len, optlen, relaylen = 0;
 	struct dhcp6 *dh6;
 
 	if (sizeof(struct dhcp6) > sizeof(replybuf)) {
@@ -933,7 +990,16 @@
 		return (-1);
 	}
 
-	dh6 = (struct dhcp6 *)replybuf;
+	if (!TAILQ_EMPTY(&optinfo->relay_list) && 
+	    (relaylen = dhcp6_set_relay((struct dhcp6_relay *) replybuf,
+	                                (struct dhcp6_relay *) (replybuf + 
+	                                                        sizeof (replybuf)),
+	                                optinfo)) < 0) {
+		dprintf(LOG_INFO, "%s" "failed to construct relay message", FNAME);
+		return (-1);
+	}
+
+	dh6 = (struct dhcp6 *) (replybuf + relaylen);
 	len = sizeof(*dh6);
 	memset(dh6, 0, sizeof(*dh6));
 	dh6->dh6_msgtypexid = origmsg->dh6_msgtypexid;
@@ -950,9 +1016,24 @@
 	}
 	len += optlen;
 
+	/*
+	 * If there were any Relay Message options, fill in the option-len
+	 * field(s) with the appropriate value(s).
+	 */
+	if (!TAILQ_EMPTY(&optinfo->relay_list))
+	    dhcp6_set_relay_option_len(optinfo, len);
+
+	len += relaylen;
+
 	/* specify the destination and send the reply */
 	dst = *sa6_any_downstream;
 	dst.sin6_addr = ((struct sockaddr_in6 *)from)->sin6_addr;
+
+	/* RELAY-REPL messages need to be directed back to the port the relay
+	   agent is listening on, namely DH6PORT_UPSTREAM */
+	if (relaylen > 0)
+		dst.sin6_port = upstream_port;
+
 	dst.sin6_scope_id = ((struct sockaddr_in6 *)from)->sin6_scope_id;
 	dprintf(LOG_DEBUG, "send destination address is %s, scope id is %d", 
 		addr2str((struct sockaddr *)&dst), dst.sin6_scope_id);
@@ -988,4 +1069,288 @@
 	timo.tv_usec = 0;
 	dhcp6_set_timer(&timo, sync_lease_timer);
 	return sync_lease_timer;
+}
+
+/* 
+ * Parse all of the RELAY-FORW messages and interface ID options. Each
+ * RELAY-FORW messages will have its hop count, link address, peer-address,
+ * and interface ID (if any) put into a relay_listval structure.
+ * A pointer to the actual original client message will be returned.
+ * If this client message cannot be found, NULL is returned to signal an error.
+ */
+static struct dhcp6 *
+dhcp6_parse_relay(relay_msg, endptr, optinfo, relay_addr)
+	struct dhcp6_relay *relay_msg;
+	struct dhcp6_relay *endptr;
+	struct dhcp6_optinfo *optinfo;
+	struct in6_addr *relay_addr;
+{
+	struct relay_listval *relay_val;
+	struct dhcp6 *relayed_msg;  /* the original message that the relay 
+	                               received */
+	struct dhcp6opt *option, *option_endptr = (struct dhcp6opt *) endptr;
+
+	u_int16_t optlen;
+	u_int16_t opt;
+
+	while ((relay_msg + 1) < endptr) {
+		relay_val = (struct relay_listval *) 
+		            calloc (1, sizeof (struct relay_listval));
+
+		if (relay_val == NULL) {
+			dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME);
+			relayfree(&optinfo->relay_list);
+			return NULL;
+		}
+
+		/* copy the msg-type, hop-count, link-address, and peer-address */
+		memcpy (&relay_val->relay, relay_msg, sizeof (struct dhcp6_relay));
+
+		/* set the msg type to relay reply now so that it doesn't need to be
+		   done when formatting the reply */
+		relay_val->relay.dh6_msg_type = DH6_RELAY_REPL;
+
+		TAILQ_INSERT_TAIL(&optinfo->relay_list, relay_val, link);
+
+		/*
+		 * need to record the first relay's link address field for later use.
+		 * The first relay is the last one we see, so keep overwriting the
+		 * relay value.
+		 */
+		memcpy (relay_addr, &relay_val->relay.link_addr, 
+		        sizeof (struct in6_addr));
+
+		/* now handle the options in the RELAY-FORW message */
+		/*
+		 * The only options that should appear in a RELAY-FORW message are:
+		 * - Interface identifier
+		 * - Relay message
+		 *
+		 * All other options are ignored.
+		 */
+		option = (struct dhcp6opt *) (relay_msg + 1);
+
+		relayed_msg = NULL; /* if this is NULL at the end of the loop, no
+		                       relayed message was found */
+
+		/* since the order of options is not specified, all of the options 
+		   must be processed */
+		while ((option + 1) < option_endptr) {
+			memcpy (&opt, &option->dh6opt_type, sizeof(opt));
+			opt = ntohs(opt);
+			memcpy (&optlen, &option->dh6opt_len, sizeof(optlen));
+			optlen = ntohs(optlen);
+
+			if ((char *) (option + 1) + optlen > (char *) option_endptr) {
+				dprintf(LOG_ERR, "%s" "invalid option length in %s option",
+				        FNAME, dhcp6optstr(opt));
+				relayfree(&optinfo->relay_list);
+				return NULL;
+			}
+
+			if (opt == DH6OPT_INTERFACE_ID) {
+				/* if this is not the first interface identifier option,
+				   then the message is incorrectly formed */
+				if (relay_val->intf_id == NULL) {
+					if (optlen) {
+						relay_val->intf_id = (struct intf_id *) 
+						                     malloc (sizeof (struct intf_id));
+						if (relay_val->intf_id == NULL) {
+							dprintf(LOG_ERR, "%s" "failed to allocate memory", 
+							        FNAME);
+							relayfree(&optinfo->relay_list);
+							return NULL;
+						}
+						else {  
+							relay_val->intf_id->intf_len = optlen;
+							relay_val->intf_id->intf_id = (char *) 
+							                              malloc (optlen);
+
+							if (relay_val->intf_id->intf_id == NULL) {
+								dprintf(LOG_ERR, "%s" 
+								                 "failed to allocate memory", 
+							            FNAME);
+								relayfree(&optinfo->relay_list);
+								return NULL;
+							}
+							else { /* copy the interface identifier so it can
+						              be sent in the reply */
+								memcpy (relay_val->intf_id->intf_id,
+								        ((char *) (option + 1)), optlen);
+							}
+						}
+					}
+					else {
+						dprintf(LOG_ERR, "%s" "Invalid length for interface "
+						                      "identifier option", FNAME);
+					}
+				}
+				else {
+					dprintf(LOG_INFO, "%s" "Multiple interface identifier "
+					                       "options in RELAY-FORW Message "
+					        FNAME);
+					relayfree(&optinfo->relay_list);
+					return NULL;
+				}
+			}
+			else if (opt == DH6OPT_RELAY_MSG) {
+				if (relayed_msg == NULL) 
+					relayed_msg = (struct dhcp6 *) (option + 1);
+				else {
+					dprintf(LOG_INFO, "%s" "Duplicated Relay Message option",
+					        FNAME);
+					relayfree(&optinfo->relay_list);
+					return NULL;
+				}
+			}
+			else   /* No other options besides interface identifier and relay
+			          message make sense, so ignore them with a warning */
+				dprintf(LOG_INFO, "%s" "Unsupported option %s found in "
+			                           "RELAY-FORW message", 
+				        FNAME, dhcp6optstr(opt));
+
+			/* advance the option pointer */
+			option = (struct dhcp6opt *) (((char *) (option + 1)) + optlen);
+		}
+
+		/*
+		 * If the relayed message is non-NULL and is a regular client
+		 * message, then the relay processing is done. If it is another
+		 * RELAY_FORW message, then continue. If the relayed message is
+		 * NULL, signal an error.
+		 */
+		if (relayed_msg != NULL && (char *) (relayed_msg + 1) <= 
+		                           (char *) endptr) {
+			/* done if have found the client message */
+			if (relayed_msg->dh6_msgtype != DH6_RELAY_FORW) 
+				return relayed_msg;
+			else 
+				relay_msg = (struct dhcp6_relay *) relayed_msg;
+		}
+		else {
+			dprintf(LOG_ERR, "%s" "invalid relayed message", FNAME);
+			relayfree(&optinfo->relay_list);
+			return NULL;
+		}
+	}
+}
+
+/*
+ * Format all of the RELAY-REPL messages and options to send back to the 
+ * client. A RELAY-REPL message and Relay Message option are added for 
+ * each of the relays that were in the RELAY-FORW packet that this is 
+ * in response to.
+ */
+static int 
+dhcp6_set_relay (msg, endptr, optinfo)
+	struct dhcp6_relay *msg;
+	struct dhcp6_relay *endptr;
+	struct dhcp6_optinfo *optinfo;
+{
+	struct relay_listval *relay;
+	struct dhcp6opt *option;
+	int relaylen = 0;
+	u_int16_t type, len;
+
+	for (relay = TAILQ_FIRST(&optinfo->relay_list); relay; 
+	     relay = TAILQ_NEXT(relay, link)) {
+		/* bounds check */
+		if (((char *) msg) + sizeof (struct dhcp6_relay) >= (char *) endptr) {
+			dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
+			        FNAME);
+			return -1;
+		}
+
+		memcpy (msg, &relay->relay, sizeof(struct dhcp6_relay));
+
+		relaylen += sizeof(struct dhcp6_relay);
+
+		option = (struct dhcp6opt *) (msg + 1);
+
+		/* include an Interface Identifier option if it was present in the
+		   original message */
+		if (relay->intf_id != NULL) {
+			/* bounds check */
+			if ((((char *) option) + sizeof(struct dhcp6opt) + 
+			    relay->intf_id->intf_len) >= (char *) endptr) {
+				dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
+				        FNAME);
+				return -1;
+			}
+			type = htons(DH6OPT_INTERFACE_ID);
+			memcpy (&option->dh6opt_type, &type, sizeof(type));
+			len = htons(relay->intf_id->intf_len);
+			memcpy (&option->dh6opt_len, &len, sizeof(len));
+			memcpy (option + 1, relay->intf_id->intf_id, 
+			        relay->intf_id->intf_len);
+			
+			option = (struct dhcp6opt *)(((char *)(option + 1)) + 
+			                             relay->intf_id->intf_len);
+			relaylen += sizeof(struct dhcp6opt) + relay->intf_id->intf_len;
+		}
+
+		/* save a pointer to the relay message option so that it is easier 
+		   to fill in the length later */
+		relay->option = option;
+
+		/* bounds check */
+		if ((char *) (option + 1) >= (char *) endptr) {
+			dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
+			        FNAME);
+			return -1;
+		}
+
+		/* lastly include the Relay Message option, which encapsulates the
+		   message being relayed */
+		type = htons(DH6OPT_RELAY_MSG);
+		memcpy (&option->dh6opt_type, &type, sizeof(type));
+		relaylen += sizeof(struct dhcp6opt);
+		/* dh6opt_len will be set by dhcp6_set_relay_option_len */
+		
+		msg = (struct dhcp6_relay *) (option + 1);
+	}
+	
+	/*
+	 * if there were no relays, this is an error since this function should
+	 * not have even been called in this case
+	 */
+	if (relaylen == 0) 
+		return -1;
+	else 
+		return relaylen;
+}
+
+/*
+ * Fill in all of the opt-len fields for the Relay Message options now that
+ * the length of the entire message is known.
+ * 
+ * len - the length of the DHCPv6 message to the client (not including any
+ *       relay options)
+ *
+ * Precondition: dhcp6_set_relay has already been called and the relay->option
+ *               fields of all of the elements in optinfo->relay_list are 
+ *               non-NULL
+ */
+static void 
+dhcp6_set_relay_option_len(optinfo, reply_msg_len)
+	struct dhcp6_optinfo *optinfo;
+	int reply_msg_len;
+{
+	struct relay_listval *relay, *last = NULL;
+	u_int16_t len;
+
+	for (relay = TAILQ_LAST(&optinfo->relay_list, relay_list);
+	     relay; relay = TAILQ_PREV(relay, relay_list, link)) {
+		if (last == NULL) {
+			len = htons(reply_msg_len);
+			memcpy (&relay->option->dh6opt_len, &len, sizeof(len));
+			last = relay;
+		}
+		else {
+			len = reply_msg_len + (((void *) (last->option + 1)) -
+			                        ((void *) (relay->option + 1)));
+			len = htons(len);
+			memcpy (&relay->option->dh6opt_len, &len, sizeof(len));
+		}
+	}
 }
Index: dhcp6s.conf.5
===================================================================
RCS file: /opt/data/qualcomm/cvsroot/dhcp6/dhcp6s.conf.5,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- dhcp6s.conf.5	21 Nov 2003 16:36:36 -0000	1.1
+++ dhcp6s.conf.5	26 Nov 2003 22:59:26 -0000	1.2
@@ -135,6 +135,14 @@
 This parameter is needed when configuring dhcp6s as the Delegation Router for
 the Prefix Delegation. dhcp6s assigns the specified prefixes 
 to the requesting routers.
+
+.nf
+.B relay\ <relay>/<prefix\ length>;
+.fi
+This statement specifies the prefix that the relay agent will put in the link
+address field of the RELAY-FORW message to indicate the link the client is
+attached to. If the link-address matches this prefix, this link declaration
+will be used. This statement is only valid in the link declaration.
 
 .nf
 .B iaidinfo
Index: server6_addr.c
===================================================================
RCS file: /opt/data/qualcomm/cvsroot/dhcp6/server6_addr.c,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- server6_addr.c	21 Nov 2003 16:36:36 -0000	1.1
+++ server6_addr.c	26 Nov 2003 22:42:47 -0000	1.3
@@ -937,8 +937,8 @@
 			continue;
 		else {
 			for (link = ifnetwork->linklist; link; link = link->next) {
-				/* without relay agent support, so far we assume
-				 * that client and server on the same link, no relay
+				/* if relay is NULL, assume client and server are on the
+				 * same link (which cannot have a relay configuration option)
 				 */
 				struct v6addrlist *temp;
 				if (relay == NULL) {
@@ -948,8 +948,11 @@
 						return link;
 				} else {
 					for (temp = link->relaylist; temp; temp = temp->next) {
-						if (IN6_ARE_ADDR_EQUAL(relay, 
-									&temp->v6addr.addr))
+						/* only compare the prefix configured to the relay
+						   link address */
+						if (!prefixcmp (relay, 
+									    &temp->v6addr.addr,
+						                temp->v6addr.plen))
 							return link;
 						else
 							continue;
Index: server6_parse.y
===================================================================
RCS file: /opt/data/qualcomm/cvsroot/dhcp6/server6_parse.y,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- server6_parse.y	21 Nov 2003 16:36:36 -0000	1.1
+++ server6_parse.y	26 Nov 2003 22:42:47 -0000	1.3
@@ -268,7 +268,6 @@
 linkbody	
 	:  
 	| linkbody linkparams
-	| relaylist 
 	;
 
 linkparams	
@@ -278,6 +277,7 @@
 	| hostdef
 	| groupdef
 	| confdecl
+	| relaylist
 	;
 
 relaylist