diff options
author | SUZUKI, Shinsuke <suz@kame.net> | 2007-03-22 05:26:21 +0000 |
---|---|---|
committer | Bjørn Mork <bjorn@mork.no> | 2010-08-06 15:37:36 +0200 |
commit | fe3d6ca944917ec34247e029218f8ca11975cc30 (patch) | |
tree | bd57455abdc29aaa0ebf02039e1985d8ca45a8be | |
parent | 484766b9467ac3a3e35eb465da6bfd825f749bd5 (diff) |
implemented DHCPv6 Decline message processing on DHCPv6 server.
-rw-r--r-- | CHANGES | 1 | ||||
-rw-r--r-- | dhcp6s.c | 201 | ||||
-rw-r--r-- | lease.c | 25 |
3 files changed, 227 insertions, 0 deletions
@@ -1,6 +1,7 @@ 2007-03-21 SUZUKI, Shinsuke <suz@kame.net> * dhcp6s.c, config.h, if.c: implemented DHCPv6 Confirm message processing on DHCPv6 server. + * dhcp6s.c, lease.c: implemented DHCPv6 Decline message processing on DHCPv6 server. 2007-03-20 SUZUKI, Shinsuke <suz@kame.net> * common.c: fixed a bug that IA-PD/IA-NA cannot coexist in one DHCP message @@ -178,6 +178,9 @@ static int react_rebind __P((struct dhcp6_if *, struct dhcp6 *, ssize_t, static int react_release __P((struct dhcp6_if *, struct in6_pktinfo *, struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *)); +static int react_decline __P((struct dhcp6_if *, struct in6_pktinfo *, + struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int, + struct relayinfolist *)); static int react_confirm __P((struct dhcp6_if *, struct in6_pktinfo *, struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *)); @@ -192,6 +195,8 @@ static int update_ia __P((int, struct dhcp6_listval *, struct dhcp6_list *, struct dhcp6_optinfo *)); static int release_binding_ia __P((struct dhcp6_listval *, struct dhcp6_list *, struct dhcp6_optinfo *)); +static int decline_binding_ia __P((struct dhcp6_listval *, struct dhcp6_list *, + struct dhcp6_optinfo *)); static int make_ia __P((struct dhcp6_listval *, struct dhcp6_list *, struct dhcp6_list *, struct host_conf *, int)); static int make_match_ia __P((struct dhcp6_listval *, struct dhcp6_list *, @@ -983,6 +988,10 @@ server6_recv(s) (void)react_release(ifp, pi, dh6, len, &optinfo, (struct sockaddr *)&from, fromlen, &relayinfohead); break; + case DH6_DECLINE: + (void)react_decline(ifp, pi, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; case DH6_CONFIRM: (void)react_confirm(ifp, pi, dh6, len, &optinfo, (struct sockaddr *)&from, fromlen, &relayinfohead); @@ -1992,6 +2001,131 @@ react_release(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) } static int +react_decline(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct in6_pktinfo *pi; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + struct dhcp6_listval *ia; + struct host_conf *client_conf; + u_int16_t stcode; + + /* message validation according to Section 15.8 of RFC3315 */ + + /* the message must include a Server Identifier option */ + if (optinfo->serverID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no server ID option"); + return (-1); + } + /* the contents of the Server Identifier option must match ours */ + if (duidcmp(&optinfo->serverID, &server_duid)) { + dprintf(LOG_INFO, FNAME, "server ID mismatch"); + return (-1); + } + /* the message must include a Client Identifier option */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no server ID option"); + return (-1); + } + + /* + * configure necessary options based on the options in request. + */ + dhcp6_init_options(&roptinfo); + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + /* copy client information back */ + if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, + "found a host configuration named %s", client_conf->name); + } + + /* process authentication */ + if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication " + "information for %s", + clientstr(client_conf, &optinfo->clientID)); + goto fail; + } + + /* + * When the server receives a Decline message via unicast from a + * client to which the server has not sent a unicast option, the server + * discards the Decline message and responds with a Reply message + * containing a Status Code option with value UseMulticast, a Server + * Identifier option containing the server's DUID, the Client + * Identifier option from the client message and no other options. + * [RFC3315 18.2.6] + * (Our current implementation never sends a unicast option.) + */ + if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + TAILQ_EMPTY(relayinfohead)) { + stcode = DH6OPT_STCODE_USEMULTICAST; + + dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s", + addr2str(from)); + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a status code"); + goto fail; + } + server6_send(DH6_REPLY, ifp, dh6, optinfo, from, + fromlen, &roptinfo, relayinfohead, client_conf); + goto end; + } + + /* + * Locates the client's binding on IA-NA and verifies that the + * information from the client matches the information stored + * for that client. (IA-PD is just ignored [RFC3633 12.1]) + */ + for (ia = TAILQ_FIRST(&optinfo->iana_list); ia; + ia = TAILQ_NEXT(ia, link)) { + if (decline_binding_ia(ia, &roptinfo.iana_list, optinfo)) + goto fail; + } + + /* + * After all the addresses have been processed, the server generates a + * Reply message and includes a Status Code option with value Success. + * [RFC3315 Section 18.2.7] + */ + stcode = DH6OPT_STCODE_SUCCESS; + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to add a status code"); + goto fail; + } + + (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, client_conf); + + end: + dhcp6_clear_options(&roptinfo); + return (0); + + fail: + dhcp6_clear_options(&roptinfo); + return (-1); +} + +static int react_confirm(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) struct dhcp6_if *ifp; struct in6_pktinfo *pi; @@ -2465,6 +2599,73 @@ release_binding_ia(iap, retlist, optinfo) return (0); } +static int +decline_binding_ia(iap, retlist, optinfo) + struct dhcp6_listval *iap; + struct dhcp6_list *retlist; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6_binding *binding; + struct dhcp6_listval *lv, *lvia; + + if ((binding = find_binding(&optinfo->clientID, DHCP6_BINDING_IA, + iap->type, iap->val_ia.iaid)) == NULL) { + /* + * For each IA in the Decline message for which the server has + * no binding information, the server adds an IA option using + * the IAID from the Release message and includes a Status Code + * option with the value NoBinding in the IA option. + */ + if (make_ia_stcode(iap->type, iap->val_ia.iaid, + DH6OPT_STCODE_NOBINDING, retlist)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make an option list"); + return (-1); + } + + return (0); + } + + /* + * If the IAs in the message are in a binding for the client and the + * addresses in the IAs have been assigned by the server to those IAs, + * the server deletes the addresses from the IAs and makes the addresses + * available for assignment to other clients. [RFC3315 Section 18.2.7] + */ + for (lv = TAILQ_FIRST(&iap->sublist); lv; + lv = TAILQ_NEXT(lv, link)) { + if (binding->iatype != DHCP6_LISTVAL_IANA) { + /* should never reach here */ + continue; + } + + if ((lvia = find_binding_ia(lv, binding)) == NULL) { + dprintf(LOG_DEBUG, FNAME, "no binding found " + "for address %s", + in6addr2str(&lvia->val_prefix6.addr, 0)); + continue; + } + + dprintf(LOG_DEBUG, FNAME, + "bound address %s has been marked as declined", + in6addr2str(&lvia->val_prefix6.addr, 0)); + decline_address(&lvia->val_prefix6.addr); + + TAILQ_REMOVE(&binding->val_list, lvia, link); + dhcp6_clear_listval(lvia); + if (TAILQ_EMPTY(&binding->val_list)) { + /* + * if the binding has become empty, + * stop procedure. + */ + remove_binding(binding); + return (0); + } + } + + return (0); +} + static void server6_signal(sig) int sig; @@ -56,8 +56,12 @@ struct hash_entry { LIST_ENTRY(hash_entry) list; char *val; + char flag; /* 0x01: DHCP6_LEASE_DECLINED */ }; +/* marked as declined (e.g. someone has been using the same address) */ +#define DHCP6_LEASE_DECLINED 0x01 + LIST_HEAD(hash_head, hash_entry); typedef unsigned int (*pfn_hash_t)(void *val) ; @@ -141,6 +145,27 @@ release_address(addr) } } +void +decline_address(addr) + struct in6_addr *addr; +{ + struct hash_entry *entry; + + if (!addr) + return; + + dprintf(LOG_DEBUG, FNAME, "addr=%s", in6addr2str(addr, 0)); + + entry = hash_table_find(&dhcp6_lease_table, addr); + if (entry == NULL) { + dprintf(LOG_WARNING, FNAME, "not found: %s", + in6addr2str(addr, 0)); + return; + } + + entry->flag |= DHCP6_LEASE_DECLINED; +} + int is_leased(addr) struct in6_addr *addr; |