]> git.decadent.org.uk Git - odhcp6c.git/commitdiff
Initial support for MAP & LW4O6 provisioning
authorSteven Barth <steven@midlink.org>
Mon, 28 Apr 2014 09:37:13 +0000 (11:37 +0200)
committerSteven Barth <steven@midlink.org>
Mon, 28 Apr 2014 09:37:13 +0000 (11:37 +0200)
CMakeLists.txt
README
src/dhcpv6.c
src/odhcp6c.h
src/script.c

index aedeebf02a90d890c977f1a4de593916e5a0d55d..e0abe0807f0aad6c79ef5d1345de2892f133874f 100644 (file)
@@ -15,6 +15,10 @@ if(${EXT_CER_ID})
        add_definitions(-DEXT_CER_ID=${EXT_CER_ID})
 endif(${EXT_CER_ID})
 
        add_definitions(-DEXT_CER_ID=${EXT_CER_ID})
 endif(${EXT_CER_ID})
 
+if(${EXT_S46})
+       add_definitions(-DEXT_S46=${EXT_S46})
+endif(${EXT_S46})
+
 if(${EXT_BFD_PING})
        add_definitions(-DEXT_BFD_PING)
        set(BFD_SOURCE src/bfd.c)
 if(${EXT_BFD_PING})
        add_definitions(-DEXT_BFD_PING)
        set(BFD_SOURCE src/bfd.c)
diff --git a/README b/README
index 6f5c62ce1508fb9fd90a6204801d133cf2e19388..6ebba7d7a65c303e41aa0f5be614a60f1182953d 100644 (file)
--- a/README
+++ b/README
@@ -30,6 +30,7 @@ especially routers. It compiles to only about 30 KB (-Os -s).
        i) DS-Lite AFTR-Name Option
        j) Prefix Class (experimental)
        k) CER-ID (experimental)
        i) DS-Lite AFTR-Name Option
        j) Prefix Class (experimental)
        k) CER-ID (experimental)
+       l) Softwire address and port mapped clients (experimental)
 
 4. Support for requesting and parsing Router Advertisements
        a) parsing of prefixes, routes, MTU and RDNSS options
 
 4. Support for requesting and parsing Router Advertisements
        a) parsing of prefixes, routes, MTU and RDNSS options
index 10f98c5aa8679cd123310ee7ae64bdc5041bf99a..873f0df6641aabf91243bd06ed6d762216deafab 100644 (file)
@@ -178,6 +178,11 @@ int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout)
 #endif
 #ifdef EXT_CER_ID
                        htons(DHCPV6_OPT_CER_ID),
 #endif
 #ifdef EXT_CER_ID
                        htons(DHCPV6_OPT_CER_ID),
+#endif
+#ifdef EXT_S46
+                       htons(DHCPV6_OPT_S46_CONT_MAPE),
+                       htons(DHCPV6_OPT_S46_CONT_MAPT),
+                       htons(DHCPV6_OPT_S46_CONT_LW),
 #endif
                };
                odhcp6c_add_state(STATE_ORO, oro, sizeof(oro));
 #endif
                };
                odhcp6c_add_state(STATE_ORO, oro, sizeof(oro));
@@ -914,6 +919,9 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                odhcp6c_clear_state(STATE_SIP_FQDN);
                odhcp6c_clear_state(STATE_AFTR_NAME);
                odhcp6c_clear_state(STATE_CER);
                odhcp6c_clear_state(STATE_SIP_FQDN);
                odhcp6c_clear_state(STATE_AFTR_NAME);
                odhcp6c_clear_state(STATE_CER);
+               odhcp6c_clear_state(STATE_S46_MAPT);
+               odhcp6c_clear_state(STATE_S46_MAPE);
+               odhcp6c_clear_state(STATE_S46_LW);
        }
 
        // Parse and find all matching IAs
        }
 
        // Parse and find all matching IAs
@@ -1019,6 +1027,14 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                        struct in6_addr any = IN6ADDR_ANY_INIT;
                        if (memcmp(&cer_id->addr, &any, sizeof(any)))
                                odhcp6c_add_state(STATE_CER, &cer_id->addr, sizeof(any));
                        struct in6_addr any = IN6ADDR_ANY_INIT;
                        if (memcmp(&cer_id->addr, &any, sizeof(any)))
                                odhcp6c_add_state(STATE_CER, &cer_id->addr, sizeof(any));
+#endif
+#ifdef EXT_S46
+               } else if (otype == DHCPV6_OPT_S46_CONT_MAPT) {
+                       odhcp6c_add_state(STATE_S46_MAPT, odata, olen);
+               } else if (otype == DHCPV6_OPT_S46_CONT_MAPE) {
+                       odhcp6c_add_state(STATE_S46_MAPE, odata, olen);
+               } else if (otype == DHCPV6_OPT_S46_CONT_LW) {
+                       odhcp6c_add_state(STATE_S46_LW, odata, olen);
 #endif
                } else if (otype != DHCPV6_OPT_CLIENTID &&
                                otype != DHCPV6_OPT_SERVERID) {
 #endif
                } else if (otype != DHCPV6_OPT_CLIENTID &&
                                otype != DHCPV6_OPT_SERVERID) {
index 2bc289b3d6198f7fae2ab185100b38b0f4ed2827..8abb6311f945ed61b65d39b5b98bfef3eb7464a7 100644 (file)
@@ -71,6 +71,17 @@ enum dhcvp6_opt {
        /* draft-donley-dhc-cer-id-option-03 */
        DHCPV6_OPT_CER_ID = EXT_CER_ID,
 #endif
        /* draft-donley-dhc-cer-id-option-03 */
        DHCPV6_OPT_CER_ID = EXT_CER_ID,
 #endif
+#ifdef EXT_S46
+       /* draft-ietf-softwire-map-dhcp-07 */
+       DHCPV6_OPT_S46_RULE = EXT_S46,
+       DHCPV6_OPT_S46_BR = EXT_S46 + 1,
+       DHCPV6_OPT_S46_DMR = EXT_S46 + 2,
+       DHCPV6_OPT_S46_V4V6BIND = EXT_S46 + 3,
+       DHCPV6_OPT_S46_PORTPARAMS = EXT_S46 + 4,
+       DHCPV6_OPT_S46_CONT_MAPE = EXT_S46 + 5,
+       DHCPV6_OPT_S46_CONT_MAPT = EXT_S46 + 6,
+       DHCPV6_OPT_S46_CONT_LW = EXT_S46 + 7,
+#endif
 };
 
 enum dhcpv6_opt_npt {
 };
 
 enum dhcpv6_opt_npt {
@@ -183,6 +194,31 @@ struct dhcpv6_cer_id {
        struct in6_addr addr;
 } _packed;
 
        struct in6_addr addr;
 } _packed;
 
+struct dhcpv6_s46_portparams {
+       uint8_t offset;
+       uint8_t psid_len;
+       uint16_t psid;
+} _packed;
+
+struct dhcpv6_s46_v4v6bind {
+       struct in_addr ipv4_address;
+       uint8_t bindprefix6_len;
+       uint8_t bind_ipv6_prefix[];
+} _packed;
+
+struct dhcpv6_s46_dmr {
+       uint8_t dmr_prefix6_len;
+       uint8_t dmr_ipv6_prefix[];
+} _packed;
+
+struct dhcpv6_s46_rule {
+       uint8_t flags;
+       uint8_t ea_len;
+       uint8_t prefix4_len;
+       struct in_addr ipv4_prefix;
+       uint8_t prefix6_len;
+       uint8_t ipv6_prefix[];
+} _packed;
 
 #define dhcpv6_for_each_option(start, end, otype, olen, odata)\
        for (uint8_t *_o = (uint8_t*)(start); _o + 4 <= (uint8_t*)(end) &&\
 
 #define dhcpv6_for_each_option(start, end, otype, olen, odata)\
        for (uint8_t *_o = (uint8_t*)(start); _o + 4 <= (uint8_t*)(end) &&\
@@ -229,6 +265,9 @@ enum odhcp6c_state {
        STATE_VENDORCLASS,
        STATE_USERCLASS,
        STATE_CER,
        STATE_VENDORCLASS,
        STATE_USERCLASS,
        STATE_CER,
+       STATE_S46_MAPT,
+       STATE_S46_MAPE,
+       STATE_S46_LW,
        _STATE_MAX
 };
 
        _STATE_MAX
 };
 
index 9089409565c3c2d37c94b81f812a848c35d4e577..847e4d7ec3a30a3b216186c7d376b104433f4802 100644 (file)
@@ -209,6 +209,102 @@ static void entry_to_env(const char *name, const void *data, size_t len, enum en
        putenv(buf);
 }
 
        putenv(buf);
 }
 
+#ifdef EXT_S46
+static void s46_to_env_portparams(const uint8_t *data, size_t len, FILE *fp)
+{
+       uint8_t *odata;
+       uint16_t otype, olen;
+       dhcpv6_for_each_option(data, &data[len], otype, olen, odata) {
+               if (otype == DHCPV6_OPT_S46_PORTPARAMS &&
+                               olen == sizeof(struct dhcpv6_s46_portparams)) {
+                       struct dhcpv6_s46_portparams *params = (void*)odata;
+                       fprintf(fp, "offset=%d,psidlen=%d,psid=%d,",
+                                       params->offset, params->psid_len, ntohs(params->psid));
+               }
+       }
+}
+#endif
+
+static void s46_to_env(enum odhcp6c_state state, const uint8_t *data, size_t len)
+{
+       const char *name = (state == STATE_S46_MAPE) ? "MAPE" :
+                       (state == STATE_S46_MAPT) ? "MAPT" : "LW4O6";
+
+       char *str;
+       size_t strsize;
+
+       FILE *fp = open_memstream(&str, &strsize);
+       fputs(name, fp);
+       fputc('=', fp);
+
+#ifdef EXT_S46
+       uint8_t *odata;
+       uint16_t otype, olen;
+       dhcpv6_for_each_option(data, &data[len], otype, olen, odata) {
+               struct dhcpv6_s46_rule *rule = (struct dhcpv6_s46_rule*)odata;
+               struct dhcpv6_s46_dmr *dmr = (struct dhcpv6_s46_dmr*)odata;
+               struct dhcpv6_s46_v4v6bind *bind = (struct dhcpv6_s46_v4v6bind*)odata;
+
+               if (state != STATE_S46_LW && otype == DHCPV6_OPT_S46_RULE &&
+                               olen >= sizeof(struct dhcpv6_s46_rule) && olen >=
+                               sizeof(struct dhcpv6_s46_rule) + rule->prefix6_len) {
+                       char buf4[INET_ADDRSTRLEN];
+                       char buf6[INET6_ADDRSTRLEN];
+                       struct in6_addr in6 = IN6ADDR_ANY_INIT;
+                       memcpy(&in6, rule->ipv6_prefix, rule->prefix6_len);
+
+                       inet_ntop(AF_INET, &rule->ipv4_prefix, buf4, sizeof(buf4));
+                       inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6));
+
+                       if (rule->flags & 1)
+                               fputs("fmr,", fp);
+
+                       fprintf(fp, "ealen=%d,prefix4len=%d,prefix6len=%d,ipv4prefix=%s,ipv6prefix=%s,",
+                                       rule->ea_len, rule->prefix4_len, rule->prefix6_len, buf4, buf6);
+
+                       s46_to_env_portparams(&rule->ipv6_prefix[rule->prefix6_len],
+                                       olen - sizeof(*rule) - rule->prefix6_len, fp);
+               } else if (state == STATE_S46_LW && otype == DHCPV6_OPT_S46_V4V6BIND &&
+                               olen >= sizeof(struct dhcpv6_s46_v4v6bind) && olen >=
+                               sizeof(struct dhcpv6_s46_v4v6bind) + bind->bindprefix6_len) {
+                       char buf4[INET_ADDRSTRLEN];
+                       char buf6[INET6_ADDRSTRLEN];
+                       struct in6_addr in6 = IN6ADDR_ANY_INIT;
+                       memcpy(&in6, bind->bind_ipv6_prefix, bind->bindprefix6_len);
+
+                       inet_ntop(AF_INET, &bind->ipv4_address, buf4, sizeof(buf4));
+                       inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6));
+
+                       fprintf(fp, "ipv4address=%s,prefix6len=%d,ipv6prefix=%s,",
+                                       buf4, bind->bindprefix6_len, buf6);
+
+                       s46_to_env_portparams(&bind->bind_ipv6_prefix[bind->bindprefix6_len],
+                                       olen - sizeof(*bind) - bind->bindprefix6_len, fp);
+               } else if (state != STATE_S46_MAPT && otype == DHCPV6_OPT_S46_BR
+                               && olen == sizeof(struct in6_addr)) {
+                       char buf6[INET6_ADDRSTRLEN];
+                       inet_ntop(AF_INET6, odata, buf6, sizeof(buf6));
+                       fprintf(fp, "br=%s,", buf6);
+               } else if (state == STATE_S46_MAPT && otype == DHCPV6_OPT_S46_DMR &&
+                               olen >= sizeof(struct dhcpv6_s46_dmr) && olen >=
+                               sizeof(struct dhcpv6_s46_dmr) + dmr->dmr_prefix6_len) {
+                       struct in6_addr in6 = IN6ADDR_ANY_INIT;
+                       memcpy(&in6, dmr->dmr_ipv6_prefix, dmr->dmr_prefix6_len);
+                       char buf6[INET6_ADDRSTRLEN];
+                       inet_ntop(AF_INET6, &in6, buf6, sizeof(buf6));
+                       fprintf(fp, "dmr=%s/%d,", buf6, dmr->dmr_prefix6_len);
+               }
+
+               fputc(' ', fp);
+       }
+#else
+       if (data && len) {}
+#endif
+
+       fclose(fp);
+       putenv(str);
+}
+
 
 static void script_call_delayed(int signal __attribute__((unused)))
 {
 
 static void script_call_delayed(int signal __attribute__((unused)))
 {
@@ -233,6 +329,7 @@ void script_call(const char *status)
 {
        size_t dns_len, search_len, custom_len, sntp_ip_len, ntp_ip_len, ntp_dns_len;
        size_t sip_ip_len, sip_fqdn_len, aftr_name_len, cer_len;
 {
        size_t dns_len, search_len, custom_len, sntp_ip_len, ntp_ip_len, ntp_dns_len;
        size_t sip_ip_len, sip_fqdn_len, aftr_name_len, cer_len;
+       size_t s46_mapt_len, s46_mape_len, s46_lw_len;
 
        odhcp6c_expire();
        if (delayed_call) {
 
        odhcp6c_expire();
        if (delayed_call) {
@@ -250,6 +347,9 @@ void script_call(const char *status)
        uint8_t *sip_fqdn = odhcp6c_get_state(STATE_SIP_FQDN, &sip_fqdn_len);
        uint8_t *aftr_name = odhcp6c_get_state(STATE_AFTR_NAME, &aftr_name_len);
        struct in6_addr *cer = odhcp6c_get_state(STATE_CER, &cer_len);
        uint8_t *sip_fqdn = odhcp6c_get_state(STATE_SIP_FQDN, &sip_fqdn_len);
        uint8_t *aftr_name = odhcp6c_get_state(STATE_AFTR_NAME, &aftr_name_len);
        struct in6_addr *cer = odhcp6c_get_state(STATE_CER, &cer_len);
+       uint8_t *s46_mapt = odhcp6c_get_state(STATE_S46_MAPT, &s46_mapt_len);
+       uint8_t *s46_mape = odhcp6c_get_state(STATE_S46_MAPE, &s46_mape_len);
+       uint8_t *s46_lw = odhcp6c_get_state(STATE_S46_LW, &s46_lw_len);
 
        size_t prefix_len, address_len, ra_pref_len, ra_route_len, ra_dns_len;
        uint8_t *prefix = odhcp6c_get_state(STATE_IA_PD, &prefix_len);
 
        size_t prefix_len, address_len, ra_pref_len, ra_route_len, ra_dns_len;
        uint8_t *prefix = odhcp6c_get_state(STATE_IA_PD, &prefix_len);
@@ -270,6 +370,9 @@ void script_call(const char *status)
                fqdn_to_env("AFTR", aftr_name, aftr_name_len);
                fqdn_to_ip_env("AFTR_IP", aftr_name, aftr_name_len);
                ipv6_to_env("CER", cer, cer_len / sizeof(*cer));
                fqdn_to_env("AFTR", aftr_name, aftr_name_len);
                fqdn_to_ip_env("AFTR_IP", aftr_name, aftr_name_len);
                ipv6_to_env("CER", cer, cer_len / sizeof(*cer));
+               s46_to_env(STATE_S46_MAPE, s46_mape, s46_mape_len);
+               s46_to_env(STATE_S46_MAPT, s46_mapt, s46_mapt_len);
+               s46_to_env(STATE_S46_LW, s46_lw, s46_lw_len);
                bin_to_env(custom, custom_len);
                entry_to_env("PREFIXES", prefix, prefix_len, ENTRY_PREFIX);
                entry_to_env("ADDRESSES", address, address_len, ENTRY_ADDRESS);
                bin_to_env(custom, custom_len);
                entry_to_env("PREFIXES", prefix, prefix_len, ENTRY_PREFIX);
                entry_to_env("ADDRESSES", address, address_len, ENTRY_ADDRESS);