Index: share/man/man4/ng_pppoe.4 =================================================================== --- share/man/man4/ng_pppoe.4 +++ share/man/man4/ng_pppoe.4 @@ -35,7 +35,7 @@ .\" $FreeBSD$ .\" $Whistle: ng_pppoe.8,v 1.1 1999/01/25 23:46:27 archie Exp $ .\" -.Dd September 15, 2015 +.Dd January 20, 2017 .Dt NG_PPPOE 4 .Os .Sh NAME @@ -104,8 +104,11 @@ It must be newly created and a service name can be given as an argument. It is legal to specify a zero-length service name, this is common on some DSL setups. -It is possible to request a connection to a specific -access concentrator by its name using the "AC-Name\\Service-Name" syntax. +It is possible to request a connection to a specific access concentrator, +and/or set a specific host uniq tag, required by some Internet providers, +using the "[AC-Name\\][Host-Uniq|]Service-Name" syntax. +To set a binary Host-Uniq, it must be encoded as a hexadecimal lowercase +string and prefixed with "0x". A session request packet will be broadcasted on the Ethernet. This command uses the .Dv ngpppoe_init_data Index: sys/netgraph/ng_pppoe.c =================================================================== --- sys/netgraph/ng_pppoe.c +++ sys/netgraph/ng_pppoe.c @@ -226,9 +226,11 @@ const struct pppoe_tag *tags[NUMTAGS]; u_int service_len; u_int ac_name_len; + u_int host_uniq_len; struct datatag service; struct datatag ac_name; + struct datatag host_uniq; }; typedef struct sess_neg *negp; @@ -589,18 +591,20 @@ pppoe_finduniq(node_p node, const struct pppoe_tag *tag) { hook_p hook = NULL; - union uniq uniq; + sessp sp; - bcopy(tag + 1, uniq.bytes, sizeof(void *)); /* Cycle through all known hooks. */ LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { /* Skip any nonsession hook. */ if (NG_HOOK_PRIVATE(hook) == NULL) continue; - if (uniq.pointer == NG_HOOK_PRIVATE(hook)) + sp = NG_HOOK_PRIVATE(hook); + if (sp->neg->host_uniq_len == ntohs(tag->tag_len) && + bcmp(sp->neg->host_uniq.data, (const char *)(tag + 1), + sp->neg->host_uniq_len) == 0) break; } - CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, uniq.pointer); + CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, sp); return (hook); } @@ -853,7 +857,10 @@ * Set up the session to the correct state and * start it. */ - int i, acnlen = 0, acnsep = 0, srvlen; + int acnpos, acnlen = 0, acnsep = 0; + int hupos, hulen = 0, husep = 0; + int i, srvpos, srvlen; + acnpos = 0; for (i = 0; i < ourmsg->data_len; i++) { if (ourmsg->data[i] == '\\') { acnlen = i; @@ -861,15 +868,56 @@ break; } } - srvlen = ourmsg->data_len - acnlen - acnsep; + hupos = acnlen + acnsep; + for (i = hupos; i < ourmsg->data_len; i++) { + if (ourmsg->data[i] == '|') { + hulen = i - hupos; + husep = 1; + break; + } + } + srvpos = hupos + hulen + husep; + srvlen = ourmsg->data_len - srvpos; - bcopy(ourmsg->data, neg->ac_name.data, acnlen); + bcopy(ourmsg->data + acnpos, neg->ac_name.data, acnlen); neg->ac_name_len = acnlen; + neg->host_uniq.hdr.tag_type = PTT_HOST_UNIQ; + if (hulen == 0) { + /* Not provided, generate one */ + neg->host_uniq.hdr.tag_len = htons(sizeof(sp)); + bcopy(&sp, neg->host_uniq.data, sizeof(sp)); + neg->host_uniq_len = sizeof(sp); + } else if (hulen > 2 && ourmsg->data[hupos] == '0' && + ourmsg->data[hupos + 1] == 'x' && hulen % 2 == 0) { + /* Hex encoded */ + static const char hexdig[16] = "0123456789abcdef"; + int j; + + neg->host_uniq.hdr.tag_len = htons((uint16_t)(hulen / 2 - 1)); + for (i = 0; i < hulen - 2; i++) { + for (j = 0; + j < 16 && + ourmsg->data[hupos + 2 + i] != hexdig[j]; + j++); + if (j == 16) + LEAVE(EINVAL); + if (i % 2 == 0) + neg->host_uniq.data[i / 2] = j << 4; + else + neg->host_uniq.data[i / 2] |= j; + } + neg->host_uniq_len = hulen / 2 - 1; + } else { + /* Plain string */ + neg->host_uniq.hdr.tag_len = htons((uint16_t)hulen); + bcopy(ourmsg->data + hupos, neg->host_uniq.data, hulen); + neg->host_uniq_len = hulen; + } + neg->service.hdr.tag_type = PTT_SRV_NAME; neg->service.hdr.tag_len = htons((uint16_t)srvlen); - bcopy(ourmsg->data + acnlen + acnsep, - neg->service.data, srvlen); + bcopy(ourmsg->data + srvpos, neg->service.data, srvlen); neg->service_len = srvlen; pppoe_start(sp); break; @@ -1061,10 +1109,6 @@ node_p node = NG_HOOK_NODE(hook); priv_p privp = NG_NODE_PRIVATE(node); negp neg = sp->neg; - struct { - struct pppoe_tag hdr; - union uniq data; - } __packed uniqtag; struct mbuf *m0; int error; @@ -1080,11 +1124,8 @@ memcpy((void *)&neg->pkt->pkt_header.eh, &privp->eh, sizeof(struct ether_header)); neg->pkt->pkt_header.ph.code = PADI_CODE; - uniqtag.hdr.tag_type = PTT_HOST_UNIQ; - uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); - uniqtag.data.pointer = sp; init_tags(sp); - insert_tag(sp, &uniqtag.hdr); + insert_tag(sp, &neg->host_uniq.hdr); insert_tag(sp, &neg->service.hdr); if (privp->max_payload.data != 0) insert_tag(sp, &privp->max_payload.hdr); @@ -1438,8 +1479,7 @@ * For now simply accept the first we receive. */ utag = get_tag(ph, PTT_HOST_UNIQ); - if ((utag == NULL) || - (ntohs(utag->tag_len) != sizeof(sp))) { + if (utag == NULL) { log(LOG_NOTICE, "ng_pppoe[%x]: no host " "unique field\n", node->nd_ID); LEAVE(ENETUNREACH); @@ -1605,8 +1645,7 @@ * set us into Session mode. */ utag = get_tag(ph, PTT_HOST_UNIQ); - if ((utag == NULL) || - (ntohs(utag->tag_len) != sizeof(sp))) { + if (utag == NULL) { LEAVE (ENETUNREACH); } sendhook = pppoe_finduniq(node, utag);