--- embedaddon/mpd/src/pppoe.c 2013/07/22 08:44:29 1.1.1.2 +++ embedaddon/mpd/src/pppoe.c 2016/11/01 09:56:12 1.1.1.3 @@ -36,7 +36,7 @@ #define ETHER_DEFAULT_HOOK NG_ETHER_HOOK_ORPHAN #ifndef SMALL_SYSTEM -#define PPPOE_MAXPARENTIFS 1024 +#define PPPOE_MAXPARENTIFS 4096 #else #define PPPOE_MAXPARENTIFS 32 #endif @@ -44,18 +44,46 @@ #define MAX_PATH 64 /* XXX should be NG_PATHSIZ */ #define MAX_SESSION 64 /* max length of PPPoE session name */ +#ifndef PTT_MAX_PAYL /* PPP-Max-Payload (RFC4638) */ +#if BYTE_ORDER == BIG_ENDIAN +#define PTT_MAX_PAYL (0x0120) +#else +#define PTT_MAX_PAYL (0x2001) +#endif +#endif + +/* https://tools.ietf.org/html/rfc4937 */ +#if BYTE_ORDER == BIG_ENDIAN +#define MPD_PTT_CREDITS (0x0106) +#define MPD_PTT_METRICS (0x0107) +#define MPD_PTT_SEQ_NUMBER (0x0108) +#define MPD_PTT_HURL (0x0111) +#define MPD_PTT_MOTM (0x0112) +#define MPD_PTT_IP_ROUTE_ADD (0x0121) +#else +#define MPD_PTT_CREDITS (0x0601) +#define MPD_PTT_METRICS (0x0701) +#define MPD_PTT_SEQ_NUMBER (0x0801) +#define MPD_PTT_HURL (0x1101) +#define MPD_PTT_MOTM (0x1201) +#define MPD_PTT_IP_ROUTE_ADD (0x2101) +#endif + /* Per link private info */ struct pppoeinfo { char path[MAX_PATH]; /* PPPoE node path */ char hook[NG_HOOKSIZ]; /* hook on that node */ char session[MAX_SESSION]; /* session name */ char acname[PPPOE_SERVICE_NAME_SIZE]; /* AC name */ + uint16_t max_payload; /* PPP-Max-Payload (RFC4638) */ + int mac_format; /* MAC address format */ u_char peeraddr[6]; /* Peer MAC address */ char real_session[MAX_SESSION]; /* real session name */ char agent_cid[64]; /* Agent Circuit ID */ char agent_rid[64]; /* Agent Remote ID */ u_char incoming; /* incoming vs. outgoing */ u_char opened; /* PPPoE opened by phys */ + u_char mp_reply; /* PPP-Max-Payload reply from server */ struct optinfo options; struct PppoeIf *PIf; /* pointer on parent ng_pppoe info */ struct PppoeList *list; @@ -69,9 +97,19 @@ static u_char gNgEtherLoaded = FALSE; enum { SET_IFACE, SET_SESSION, - SET_ACNAME + SET_ACNAME, + SET_MAX_PAYLOAD, + SET_MAC_FORMAT }; +/* MAC format options */ +enum { + MAC_UNFORMATTED = 0, + MAC_UNIX_LIKE, + MAC_CISCO_LIKE, + MAC_IETF +}; + /* Invariants: ---------- @@ -113,6 +151,8 @@ static int PppoeCallingNum(Link l, void *buf, size_t b static int PppoeCalledNum(Link l, void *buf, size_t buf_len); static int PppoeSelfName(Link l, void *buf, size_t buf_len); static int PppoePeerName(Link l, void *buf, size_t buf_len); +static u_short PppoeGetMtu(Link l, int conf); +static u_short PppoeGetMru(Link l, int conf); static void PppoeCtrlReadEvent(int type, void *arg); static void PppoeConnectTimeout(void *arg); static void PppoeStat(Context ctx); @@ -155,6 +195,8 @@ const struct phystype gPppoePhysType = { .callednum = PppoeCalledNum, .selfname = PppoeSelfName, .peername = PppoePeerName, + .getmtu = PppoeGetMtu, + .getmru = PppoeGetMru }; const struct cmdtab PppoeSetCmds[] = { @@ -164,7 +206,13 @@ const struct cmdtab PppoeSetCmds[] = { PppoeSetCommand, NULL, 2, (void *)SET_SESSION }, { "acname {name}", "Set PPPoE access concentrator name", PppoeSetCommand, NULL, 2, (void *)SET_ACNAME }, - { NULL }, +#ifdef NGM_PPPOE_SETMAXP_COOKIE + { "max-payload {size}", "Set PPP-Max-Payload tag", + PppoeSetCommand, NULL, 2, (void *)SET_MAX_PAYLOAD }, +#endif + { "mac-format {format}", "Set RADIUS attribute 31 MAC format", + PppoeSetCommand, NULL, 2, (void *)SET_MAC_FORMAT }, + { NULL } }; /* @@ -191,6 +239,35 @@ struct PppoeIf { int PppoeIfCount=0; struct PppoeIf PppoeIfs[PPPOE_MAXPARENTIFS]; +struct tagname { + int tag; + const char *name; +}; + +static const struct tagname tag2str[] = { + { PTT_EOL, "End-Of-List" }, + { PTT_SRV_NAME, "Service-Name" }, + { PTT_AC_NAME, "AC-Name" }, + { PTT_HOST_UNIQ, "Host-Uniq" }, + { PTT_AC_COOKIE, "AC-Cookie" }, + { PTT_VENDOR, "Vendor-Specific" }, + { PTT_RELAY_SID, "Relay-Session-Id" }, + { PTT_MAX_PAYL, "PPP-Max-Payload" }, + { PTT_SRV_ERR, "Service-Name-Error" }, + { PTT_SYS_ERR, "AC-System-Error" }, + { PTT_GEN_ERR, "Generic-Error" }, + /* RFC 4937 */ + { MPD_PTT_CREDITS, "Credits" }, + { MPD_PTT_METRICS, "Metrics" }, + { MPD_PTT_SEQ_NUMBER, "Sequence Number" }, + { MPD_PTT_HURL, "HURL" }, + { MPD_PTT_MOTM, "MOTM" }, + { MPD_PTT_IP_ROUTE_ADD, "IP_Route_Add" }, + { 0, "UNKNOWN" } +}; +#define NUM_TAG_NAMES (sizeof(tag2str) / sizeof(*tag2str)) + + /* * PppoeInit() * @@ -213,6 +290,9 @@ PppoeInit(Link l) pe->agent_cid[0] = 0; pe->agent_rid[0] = 0; pe->PIf = NULL; + pe->max_payload = 0; + pe->mac_format = MAC_UNFORMATTED; + pe->mp_reply = 0; /* Done */ return(0); @@ -327,11 +407,23 @@ PppoeOpen(Link l) l->name, path, cn.ourhook, cn.path, cn.peerhook); goto fail2; } + +#ifdef NGM_PPPOE_SETMAXP_COOKIE + if (pe->max_payload > 0) + Log(LG_PHYS, ("[%s] PPPoE: Set PPP-Max-Payload to '%u'", + l->name, pe->max_payload)); + /* Tell the PPPoE node to set PPP-Max-Payload value (unset if 0). */ + if (NgSendMsg(pe->PIf->csock, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SETMAXP, + &pe->max_payload, sizeof(uint16_t)) < 0) { + Perror("[%s] PPPoE can't set PPP-Max-Payload value", l->name); + goto fail2; + } +#endif Log(LG_PHYS, ("[%s] PPPoE: Connecting to '%s'", l->name, pe->session)); /* Tell the PPPoE node to try to connect to a server. */ - memset(idata, 0, sizeof(idata)); + memset(idata, 0, sizeof(struct ngpppoe_init_data)); strlcpy(idata->hook, session_hook, sizeof(idata->hook)); idata->data_len = strlen(pe->session); strncpy(idata->data, pe->session, MAX_SESSION); @@ -351,6 +443,7 @@ PppoeOpen(Link l) strlcpy(pe->real_session, pe->session, sizeof(pe->real_session)); pe->agent_cid[0] = 0; pe->agent_rid[0] = 0; + pe->mp_reply = 0; return; fail3: @@ -433,6 +526,7 @@ PppoeDoClose(Link l) pi->real_session[0] = 0; pi->agent_cid[0] = 0; pi->agent_rid[0] = 0; + pi->mp_reply = 0; } /* @@ -444,7 +538,11 @@ static void PppoeCtrlReadEvent(int type, void *arg) { union { +#ifdef NGM_PPPOE_SETMAXP_COOKIE + u_char buf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_maxp)]; +#else u_char buf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)]; +#endif struct ng_mesg resp; } u; char path[NG_PATHSIZ]; @@ -468,6 +566,9 @@ PppoeCtrlReadEvent(int type, void *arg) case NGM_PPPOE_SUCCESS: case NGM_PPPOE_FAIL: case NGM_PPPOE_CLOSE: +#ifdef NGM_PPPOE_SETMAXP_COOKIE + case NGM_PPPOE_SETMAXP: +#endif { char ppphook[NG_HOOKSIZ]; char *linkname, *rest; @@ -510,6 +611,8 @@ PppoeCtrlReadEvent(int type, void *arg) /* Decode message. */ switch (u.resp.header.cmd) { case NGM_PPPOE_SESSIONID: /* XXX: I do not know what to do with this? */ + Log(LG_PHYS3, ("PPPoE: rec'd SESSIONID %u from \"%s\"", + ntohs((uint16_t)u.resp.data), path)); break; case NGM_PPPOE_SUCCESS: Log(LG_PHYS, ("[%s] PPPoE: connection successful", l->name)); @@ -535,6 +638,28 @@ PppoeCtrlReadEvent(int type, void *arg) Log(LG_PHYS, ("PPPoE: rec'd ACNAME \"%s\"", ((struct ngpppoe_sts *)u.resp.data)->hook)); break; +#ifdef NGM_PPPOE_SETMAXP_COOKIE + case NGM_PPPOE_SETMAXP: + { + struct ngpppoe_maxp *maxp; + + maxp = ((struct ngpppoe_maxp *)u.resp.data); + Log(LG_PHYS, ("[%s] PPPoE: rec'd PPP-Max-Payload '%u'", + l->name, maxp->data)); + if (pi->max_payload > 0) { + if (pi->max_payload == maxp->data) + pi->mp_reply = 1; + else + Log(LG_PHYS, + ("[%s] PPPoE: sent and returned values are not equal", + l->name)); + } else + Log(LG_PHYS, ("[%s] PPPoE: server sent tag PPP-Max-Payload" + " without request from the client", + l->name)); + break; + } +#endif default: Log(LG_PHYS, ("PPPoE: rec'd command %lu from \"%s\"", (u_long)u.resp.header.cmd, path)); @@ -551,10 +676,32 @@ PppoeStat(Context ctx) const PppoeInfo pe = (PppoeInfo)ctx->lnk->info; char buf[32]; + switch (pe->mac_format) { + case MAC_UNFORMATTED: + sprintf(buf, "unformatted"); + break; + case MAC_UNIX_LIKE: + sprintf(buf, "unix-like"); + break; + case MAC_CISCO_LIKE: + sprintf(buf, "cisco-like"); + break; + case MAC_IETF: + sprintf(buf, "ietf"); + break; + default: + sprintf(buf, "unknown"); + break; + } + Printf("PPPoE configuration:\r\n"); Printf("\tIface Node : %s\r\n", pe->path); Printf("\tIface Hook : %s\r\n", pe->hook); Printf("\tSession : %s\r\n", pe->session); +#ifdef NGM_PPPOE_SETMAXP_COOKIE + Printf("\tMax-Payload : %u\r\n", pe->max_payload); +#endif + Printf("\tMAC format : %s\r\n", buf); Printf("PPPoE status:\r\n"); if (ctx->lnk->state != PHYS_STATE_DOWN) { Printf("\tOpened : %s\r\n", (pe->opened?"YES":"NO")); @@ -562,6 +709,7 @@ PppoeStat(Context ctx) PppoePeerMacAddr(ctx->lnk, buf, sizeof(buf)); Printf("\tCurrent peer : %s\r\n", buf); Printf("\tSession : %s\r\n", pe->real_session); + Printf("\tMax-Payload : %s\r\n", (pe->mp_reply?"YES":"NO")); Printf("\tCircuit-ID : %s\r\n", pe->agent_cid); Printf("\tRemote-ID : %s\r\n", pe->agent_rid); } @@ -615,7 +763,30 @@ PppoeCallingNum(Link l, void *buf, size_t buf_len) PppoeInfo const pppoe = (PppoeInfo)l->info; if (pppoe->incoming) { - ether_ntoa_r((struct ether_addr *)pppoe->peeraddr, buf); + switch (pppoe->mac_format) { + case MAC_UNFORMATTED: + snprintf(buf, buf_len, "%02x%02x%02x%02x%02x%02x", + pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2], + pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]); + break; + case MAC_UNIX_LIKE: + ether_ntoa_r((struct ether_addr *)pppoe->peeraddr, buf); + break; + case MAC_CISCO_LIKE: + snprintf(buf, buf_len, "%02x%02x.%02x%02x.%02x%02x", + pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2], + pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]); + break; + case MAC_IETF: + snprintf(buf, buf_len, "%02x-%02x-%02x-%02x-%02x-%02x", + pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2], + pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]); + break; + default: + sprintf(buf, "unknown"); + return(-1); + break; + } } else { strlcpy(buf, pppoe->real_session, buf_len); } @@ -629,7 +800,30 @@ PppoeCalledNum(Link l, void *buf, size_t buf_len) PppoeInfo const pppoe = (PppoeInfo)l->info; if (!pppoe->incoming) { - ether_ntoa_r((struct ether_addr *)pppoe->peeraddr, buf); + switch (pppoe->mac_format) { + case MAC_UNFORMATTED: + snprintf(buf, buf_len, "%02x%02x%02x%02x%02x%02x", + pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2], + pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]); + break; + case MAC_UNIX_LIKE: + ether_ntoa_r((struct ether_addr *)pppoe->peeraddr, buf); + break; + case MAC_CISCO_LIKE: + snprintf(buf, buf_len, "%02x%02x.%02x%02x.%02x%02x", + pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2], + pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]); + break; + case MAC_IETF: + snprintf(buf, buf_len, "%02x-%02x-%02x-%02x-%02x-%02x", + pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2], + pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]); + break; + default: + sprintf(buf, "unknown"); + return(-1); + break; + } } else { strlcpy(buf, pppoe->real_session, buf_len); } @@ -657,6 +851,34 @@ PppoePeerName(Link l, void *buf, size_t buf_len) return (0); } +static u_short +PppoeGetMtu(Link l, int conf) +{ + PppoeInfo const pppoe = (PppoeInfo)l->info; + + if (pppoe->max_payload > 0 && pppoe->mp_reply > 0) + return (pppoe->max_payload); + else + if (conf == 0) + return (l->type->mtu); + else + return (l->conf.mtu); +} + +static u_short +PppoeGetMru(Link l, int conf) +{ + PppoeInfo const pppoe = (PppoeInfo)l->info; + + if (pppoe->max_payload > 0 && pppoe->mp_reply > 0) + return (pppoe->max_payload); + else + if (conf == 0) + return (l->type->mru); + else + return (l->conf.mru); +} + static int CreatePppoeNode(struct PppoeIf *PIf, const char *path, const char *hook) { @@ -667,7 +889,7 @@ CreatePppoeNode(struct PppoeIf *PIf, const char *path, struct ng_mesg *resp; const struct hooklist *hlist; const struct nodeinfo *ninfo; - int f; + uint32_t f; /* Make sure interface is up. */ char iface[IFNAMSIZ]; @@ -892,6 +1114,115 @@ get_vs_tag(const struct pppoe_hdr* ph, uint32_t idx) } static void +print_tags(const struct pppoe_hdr* ph) +{ + const char *const end = ((const char *)(ph + 1)) + + ntohs(ph->length); + const struct pppoe_tag *pt = (const void *)(ph + 1); + const char *ptn; + const void *v; + char buf[1024], tag[32]; + size_t len, k; + + /* + * Keep processing tags while a tag header will still fit. + */ + while((const char*)(pt + 1) <= end) { + /* + * If the tag data would go past the end of the packet, abort. + */ + v = pt + 1; + ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); + if (ptn > end) + return; + len = ntohs(pt->tag_len); + buf[0] = 0; + switch (pt->tag_type) { + case PTT_EOL: + if (len != 0) + sprintf(buf, "TAG_LENGTH is not zero!"); + break; + case PTT_SRV_NAME: + if (len >= sizeof(buf)) + len = sizeof(buf)-1; + memcpy(buf, pt + 1, len); + buf[len] = 0; + if (len == 0) + sprintf(buf, "Any service is acceptable"); + break; + case PTT_AC_NAME: + if (len >= sizeof(buf)) + len = sizeof(buf)-1; + memcpy(buf, pt + 1, len); + buf[len] = 0; + break; + case PTT_HOST_UNIQ: + case PTT_AC_COOKIE: + case PTT_RELAY_SID: + snprintf(buf, sizeof(buf), "0x%s", Bin2Hex(v, len)); + break; + case PTT_VENDOR: + if (len >= 4) { + if ((uint8_t)*(uint8_t*)v != 0) { + snprintf(buf, sizeof(buf), + "First byte of VENDOR is not zero! 0x%s", + Bin2Hex(v, len)); + } else { + snprintf(buf, sizeof(buf), "0x%s 0x%s", + Bin2Hex(v, 4), + Bin2Hex((const uint8_t*)v + 4, len - 4)); + } + } else { + sprintf(buf, "TAG_LENGTH must be >= 4 !"); + } + break; + case PTT_MAX_PAYL: + if (len != 2) { + sprintf(buf, "TAG_LENGTH is not 2!"); + } else { + sprintf(buf, "%u", *(uint16_t*)(pt + 1)); + } + break; + case PTT_SRV_ERR: + if (len > 0 && (const char *)(pt + 1)+4 !=0) { + if (len >= sizeof(buf)) + len = sizeof(buf)-1; + memcpy(buf, pt + 1, len); + buf[len] = 0; + } + break; + case PTT_SYS_ERR: + case PTT_GEN_ERR: + if (len >= sizeof(buf)) + len = sizeof(buf)-1; + memcpy(buf, pt + 1, len); + buf[len] = 0; + break; + case MPD_PTT_CREDITS: + case MPD_PTT_METRICS: + case MPD_PTT_SEQ_NUMBER: + case MPD_PTT_HURL: + case MPD_PTT_MOTM: + case MPD_PTT_IP_ROUTE_ADD: + sprintf(buf, "Not implemented"); + break; + default: + sprintf(buf, "0x%04x", pt->tag_type); + break; + } + /* First check our stat list for known tags */ + for (k = 0; k < NUM_TAG_NAMES; k++) { + if (pt->tag_type == tag2str[k].tag) { + sprintf(tag, "%s", tag2str[k].name); + break; + } + } + Log(LG_PHYS3, ("TAG: %s, Value: %s", tag, buf)); + pt = (const struct pppoe_tag*)ptn; + } +} + +static void PppoeListenEvent(int type, void *arg) { int k, sz; @@ -975,10 +1306,14 @@ PppoeListenEvent(int type, void *arg) pos += 2 + len1; } } + Log(LG_PHYS, ("Incoming PPPoE connection request via %s for " "service \"%s\" from %s", PIf->ifnodepath, real_session, ether_ntoa((struct ether_addr *)&wh->eh.ether_shost))); + if (gLogOptions & LG_PHYS3) + print_tags(ph); + if (gShutdownInProgress) { Log(LG_PHYS, ("Shutdown sequence in progress, ignoring request.")); return; @@ -1253,9 +1588,8 @@ PppoeListen(Link l) if (NgSendMsg(PIf->csock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) { - Log(LG_ERR, ("PPPoE: Can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s", - ".:", cn.ourhook, cn.path, cn.peerhook, - strerror(errno))); + Perror("PPPoE: Can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"", + ".:", cn.ourhook, cn.path, cn.peerhook); return(0); } @@ -1340,7 +1674,9 @@ PppoeSetCommand(Context ctx, int ac, char *av[], void const PppoeInfo pi = (PppoeInfo) ctx->lnk->info; const char *hookname = ETHER_DEFAULT_HOOK; const char *colon; - +#ifdef NGM_PPPOE_SETMAXP_COOKIE + int ap; +#endif switch ((intptr_t)arg) { case SET_IFACE: switch (ac) { @@ -1376,6 +1712,31 @@ PppoeSetCommand(Context ctx, int ac, char *av[], void if (ac != 1) return(-1); strlcpy(pi->acname, av[0], sizeof(pi->acname)); + break; +#ifdef NGM_PPPOE_SETMAXP_COOKIE + case SET_MAX_PAYLOAD: + if (ac != 1) + return(-1); + ap = atoi(av[0]); + if (ap < PPPOE_MRU || ap > ETHER_MAX_LEN - 8) + Error("PPP-Max-Payload value \"%s\"", av[0]); + pi->max_payload = ap; + break; +#endif + case SET_MAC_FORMAT: + if (ac != 1) + return(-1); + if (strcmp(av[0], "unformatted") == 0) { + pi->mac_format = MAC_UNFORMATTED; + } else if (strcmp(av[0], "unix-like") == 0) { + pi->mac_format = MAC_UNIX_LIKE; + } else if (strcmp(av[0], "cisco-like") == 0) { + pi->mac_format = MAC_CISCO_LIKE; + } else if (strcmp(av[0], "ietf") == 0) { + pi->mac_format = MAC_IETF; + } else { + Error("Incorrect PPPoE mac-format \"%s\"", av[0]); + } break; default: assert(0);