/* * bund.c * * Written by Archie Cobbs * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved. * See ``COPYRIGHT.whistle'' * * Bundle handling stuff */ #include "ppp.h" #include "bund.h" #include "ipcp.h" #include "ccp.h" #include "mp.h" #include "iface.h" #include "link.h" #include "msg.h" #include "custom.h" #include "ngfunc.h" #include "log.h" #include "util.h" #include "input.h" #include #include #ifdef __DragonFly__ #include #include #include #else #include #include #include #endif /* * DEFINITIONS */ /* #define DEBUG_BOD */ #define BUND_REOPEN_DELAY 3 /* wait this long before closing */ #define BUND_REOPEN_PAUSE 3 /* wait this long before re-opening */ #define BUND_MIN_TOT_BW 9600 /* Set menu options */ enum { SET_PERIOD, SET_LOW_WATER, SET_HIGH_WATER, SET_MIN_CONNECT, SET_MIN_DISCONNECT, SET_AUTHNAME, SET_PASSWORD, SET_RETRY, SET_ACCEPT, SET_DENY, SET_ENABLE, SET_DISABLE, SET_YES, SET_NO, }; /* * INTERNAL FUNCTIONS */ static int BundNgInit(Bund b, const char *reqIface); static void BundNgShutdown(Bund b, int iface, int ppp); static void BundNgDataEvent(int type, void *cookie); static void BundBmStart(Bund b); static void BundBmStop(Bund b); static void BundBmTimeout(void *arg); static Bund BundFind(char *name); static void BundReasses(Bund b, int add); static int BundSetCommand(Context ctx, int ac, char *av[], void *arg); static void BundShowLinks(Context ctx, Bund sb); static void BundNcpsUp(Bund b); static void BundNcpsDown(Bund b); static void BundReOpenLinks(void *arg); static void BundCloseLink(Link l); static void BundMsg(int type, void *cookie); /* * GLOBAL VARIABLES */ struct discrim self_discrim; const struct cmdtab BundSetCmds[] = { { "period seconds", "BOD sampling period", BundSetCommand, NULL, (void *) SET_PERIOD }, { "lowat percent", "BOD low water mark", BundSetCommand, NULL, (void *) SET_LOW_WATER }, { "hiwat percent", "BOD high water mark", BundSetCommand, NULL, (void *) SET_HIGH_WATER }, { "min-con seconds", "BOD min connected time", BundSetCommand, NULL, (void *) SET_MIN_CONNECT }, { "min-dis seconds", "BOD min disconnected time", BundSetCommand, NULL, (void *) SET_MIN_DISCONNECT }, { "retry seconds", "FSM retry timeout", BundSetCommand, NULL, (void *) SET_RETRY }, { "accept [opt ...]", "Accept option", BundSetCommand, NULL, (void *) SET_ACCEPT }, { "deny [opt ...]", "Deny option", BundSetCommand, NULL, (void *) SET_DENY }, { "enable [opt ...]", "Enable option", BundSetCommand, NULL, (void *) SET_ENABLE }, { "disable [opt ...]", "Disable option", BundSetCommand, NULL, (void *) SET_DISABLE }, { "yes [opt ...]", "Enable and accept option", BundSetCommand, NULL, (void *) SET_YES }, { "no [opt ...]", "Disable and deny option", BundSetCommand, NULL, (void *) SET_NO }, { NULL }, }; /* * INTERNAL VARIABLES */ static const struct confinfo gConfList[] = { { 0, BUND_CONF_MULTILINK, "multilink" }, { 1, BUND_CONF_SHORTSEQ, "shortseq" }, { 0, BUND_CONF_IPCP, "ipcp" }, { 0, BUND_CONF_IPV6CP, "ipv6cp" }, { 0, BUND_CONF_COMPRESSION, "compression" }, { 0, BUND_CONF_ENCRYPTION, "encryption" }, { 0, BUND_CONF_CRYPT_REQD, "crypt-reqd" }, { 0, BUND_CONF_BWMANAGE, "bw-manage" }, { 0, BUND_CONF_ROUNDROBIN, "round-robin" }, { 0, BUND_CONF_NORETRY, "noretry" }, { 0, 0, NULL }, }; /* * BundOpen() */ void BundOpen(Bund b) { MsgSend(b->msgs, MSG_OPEN, b); } /* * BundClose() */ void BundClose(Bund b) { MsgSend(b->msgs, MSG_CLOSE, b); } /* * BundOpenCmd() */ void BundOpenCmd(Context ctx) { MsgSend(ctx->bund->msgs, MSG_OPEN, ctx->bund); } /* * BundCloseCmd() */ void BundCloseCmd(Context ctx) { MsgSend(ctx->bund->msgs, MSG_CLOSE, ctx->bund); } /* * BundJoin() * * This is called when a link enters the NETWORK phase. * * Verify that link is OK to come up as part of it's bundle. * If so, join it to the bundle. Returns FALSE if there's a problem. * If this is the first link to join, and it's not supporting * multi-link, then prevent any further links from joining. * * Right now this is fairly simple minded: you have to define * the links in a bundle first, then stick to that plan. For * a server this might be too restrictive a policy. * * Returns zero if fails, otherwise the new number of up links. */ int BundJoin(Link l) { Bund b = l->bund; BundBm const bm = &l->bund->bm; LcpState const lcp = &l->lcp; if (gShutdownInProgress) { Log(LG_PHYS, ("Shutdown sequence in progress, BundJoin() denied")); return(0); } if (!b->open) b->open = TRUE; /* Open bundle on incoming */ /* Other links in this bundle yet? If so, enforce bundling */ if (bm->n_up > 0) { /* First of all, we have to be doing multi-link */ if (!b->multilink || !lcp->peer_multilink) { Log(LG_LCP, ("[%s] multi-link is not active on this bundle", l->name)); return(0); } /* Discriminator and authname must match */ if (!MpDiscrimEqual(&l->peer_discrim, &b->peer_discrim)) { Log(LG_LCP, ("[%s] multi-link peer discriminator mismatch", l->name)); return(0); } if (strcmp(l->lcp.auth.params.authname, b->params.authname)) { Log(LG_LCP, ("[%s] multi-link peer authorization name mismatch", l->name)); return(0); } } else { /* Cancel re-open timer; we've come up somehow (eg, LCP renegotiation) */ TimerStop(&b->reOpenTimer); /* Copy auth params from the first link */ authparamsCopy(&l->lcp.auth.params,&b->params); /* Initialize multi-link stuff */ if ((b->multilink = lcp->peer_multilink)) { b->peer_discrim = l->peer_discrim; MpInit(b, l); } /* Start bandwidth management */ BundBmStart(b); } /* Reasses MTU, bandwidth, etc. */ BundReasses(b, 1); /* Configure this link */ b->pppConfig.links[l->bundleIndex].enableLink = 1; b->pppConfig.links[l->bundleIndex].mru = lcp->peer_mru; b->pppConfig.links[l->bundleIndex].enableACFComp = lcp->peer_acfcomp; b->pppConfig.links[l->bundleIndex].enableProtoComp = lcp->peer_protocomp; b->pppConfig.links[l->bundleIndex].bandwidth = (l->bandwidth / 8 + 5) / 10; b->pppConfig.links[l->bundleIndex].latency = (l->latency + 500) / 1000; /* What to do when the first link comes up */ if (bm->n_up == 1) { /* Configure the bundle */ b->pppConfig.bund.enableMultilink = lcp->peer_multilink; b->pppConfig.bund.mrru = lcp->peer_mrru; b->pppConfig.bund.xmitShortSeq = lcp->peer_shortseq; b->pppConfig.bund.recvShortSeq = lcp->want_shortseq; b->pppConfig.bund.enableRoundRobin = Enabled(&b->conf.options, BUND_CONF_ROUNDROBIN); /* generate a uniq session id */ snprintf(b->msession_id, AUTH_MAX_SESSIONID, "%d-%s", (int)(time(NULL) % 10000000), b->name); b->originate = l->originate; } /* Update PPP node configuration */ NgFuncSetConfig(b); /* copy multysession-id to link */ strncpy(l->msession_id, b->msession_id, sizeof(l->msession_id)); /* generate a uniq session id */ snprintf(l->session_id, AUTH_MAX_SESSIONID, "%d-%s", (int)(time(NULL) % 10000000), l->name); /* What to do when the first link comes up */ if (bm->n_up == 1) { BundNcpsOpen(b); BundNcpsUp(b); BundResetStats(b); #ifndef NG_PPP_STATS64 /* starting bundle statistics timer */ TimerInit(&b->statsUpdateTimer, "BundUpdateStats", BUND_STATS_UPDATE_INTERVAL, BundUpdateStatsTimer, b); TimerStartRecurring(&b->statsUpdateTimer); #endif } AuthAccountStart(l, AUTH_ACCT_START); /* Done */ return(bm->n_up); } /* * BundLeave() * * This is called when a link leaves the NETWORK phase. */ void BundLeave(Link l) { Bund b = l->bund; BundBm const bm = &b->bm; /* Elvis has left the bundle */ assert(bm->n_up > 0); AuthAccountStart(l, AUTH_ACCT_STOP); BundReasses(b, 0); /* Disable link */ b->pppConfig.links[l->bundleIndex].enableLink = 0; NgFuncSetConfig(b); /* Special stuff when last link goes down... */ if (bm->n_up == 0) { #ifndef NG_PPP_STATS64 /* stopping bundle statistics timer */ TimerStop(&b->statsUpdateTimer); #endif /* Reset statistics and auth information */ BundBmStop(b); BundNcpsClose(b); BundNcpsDown(b); authparamsDestroy(&b->params); memset(&b->ccp.mppc, 0, sizeof(b->ccp.mppc)); /* try to open again later */ if (b->open && !Enabled(&b->conf.options, BUND_CONF_NORETRY) && !gShutdownInProgress) { /* wait BUND_REOPEN_DELAY to see if it comes back up */ int delay = BUND_REOPEN_DELAY; delay += ((random() ^ gPid ^ time(NULL)) & 1); Log(LG_BUND, ("[%s] Last link has gone and no noretry option, will reopen in %d seconds", b->name, delay)); TimerStop(&b->reOpenTimer); TimerInit(&b->reOpenTimer, "BundReOpen", delay * SECONDS, BundReOpenLinks, b); TimerStart(&b->reOpenTimer); } else if (b->open) { b->open = FALSE; } } } /* * BundReOpenLinks() * * The last link went down, and we waited BUND_REOPEN_DELAY seconds for * it to come back up. It didn't, so close all the links and re-open them * BUND_REOPEN_PAUSE seconds from now. * * The timer calling this is cancelled whenever any link comes up. */ static void BundReOpenLinks(void *arg) { Bund b = (Bund)arg; Log(LG_BUND, ("[%s] Last link has gone and no noretry option, reopening in %d seconds", b->name, BUND_REOPEN_PAUSE)); BundCloseLinks(b); TimerStop(&b->reOpenTimer); TimerInit(&b->reOpenTimer, "BundOpen", BUND_REOPEN_PAUSE * SECONDS, (void (*)(void *)) BundOpenLinks, b); TimerStart(&b->reOpenTimer); RecordLinkUpDownReason(b, NULL, 1, STR_REDIAL, NULL); } /* * BundMsg() * * Deal with incoming message to the bundle */ static void BundMsg(int type, void *arg) { Bund b = (Bund)arg; Log(LG_BUND, ("[%s] bundle: %s event in state %s", b->name, MsgName(type), b->open ? "OPENED" : "CLOSED")); TimerStop(&b->reOpenTimer); switch (type) { case MSG_OPEN: b->open = TRUE; break; case MSG_CLOSE: b->open = FALSE; BundCloseLinks(b); break; default: assert(FALSE); } } /* * BundOpenLinks() * * Open one link or all links, depending on whether bandwidth * management is in effect or not. */ void BundOpenLinks(Bund b) { TimerStop(&b->reOpenTimer); if (Enabled(&b->conf.options, BUND_CONF_BWMANAGE)) { if (!b->bm.links_open || b->bm.n_open == 0) BundOpenLink(b->links[0]); } else { int k; for (k = 0; k < b->n_links; k++) BundOpenLink(b->links[k]); } } /* * BundOpenLink() */ void BundOpenLink(Link l) { Log(LG_BUND, ("[%s] opening link \"%s\"...", l->bund->name, l->name)); LinkOpen(l); l->bund->bm.links_open = 1; } /* * BundCloseLinks() * * Close all links */ void BundCloseLinks(Bund b) { int k; TimerStop(&b->reOpenTimer); for (k = 0; k < b->n_links; k++) if (OPEN_STATE(b->links[k]->lcp.fsm.state)) BundCloseLink(b->links[k]); b->bm.links_open = 0; } /* * BundCloseLink() */ static void BundCloseLink(Link l) { Log(LG_BUND, ("[%s] closing link \"%s\"...", l->bund->name, l->name)); LinkClose(l); } /* * BundNcpsOpen() */ void BundNcpsOpen(Bund b) { if (Enabled(&b->conf.options, BUND_CONF_IPCP)) IpcpOpen(b); if (Enabled(&b->conf.options, BUND_CONF_IPV6CP)) Ipv6cpOpen(b); if (Enabled(&b->conf.options, BUND_CONF_COMPRESSION)) CcpOpen(b); if (Enabled(&b->conf.options, BUND_CONF_ENCRYPTION)) EcpOpen(b); } /* * BundNcpsUp() */ static void BundNcpsUp(Bund b) { if (Enabled(&b->conf.options, BUND_CONF_IPCP)) IpcpUp(b); if (Enabled(&b->conf.options, BUND_CONF_IPV6CP)) Ipv6cpUp(b); if (Enabled(&b->conf.options, BUND_CONF_COMPRESSION)) CcpUp(b); if (Enabled(&b->conf.options, BUND_CONF_ENCRYPTION)) EcpUp(b); } void BundNcpsStart(Bund b, int proto) { b->ncpstarted |= ((1<>1); } void BundNcpsFinish(Bund b, int proto) { b->ncpstarted &= (~((1<>1)); if (!b->ncpstarted) { Log(LG_BUND, ("[%s] No NCPs left. Closing links...", b->name)); RecordLinkUpDownReason(b, NULL, 0, STR_PROTO_ERR, NULL); BundCloseLinks(b); /* We have nothing to live for */ } } void BundNcpsJoin(Bund b, int proto) { IfaceState iface = &b->iface; if (iface->dod) { if (iface->ip_up) { iface->ip_up=0; IfaceIpIfaceDown(b); } if (iface->ipv6_up) { iface->ipv6_up=0; IfaceIpv6IfaceDown(b); } iface->dod = 0; iface->up = 0; IfaceDown(b); } if (!iface->up) { iface->up=1; if (proto == NCP_NONE) { iface->dod=1; IfaceUp(b, 0); } else { IfaceUp(b, 1); } } switch(proto) { case NCP_IPCP: if (!iface->ip_up) { iface->ip_up=1; IfaceIpIfaceUp(b, 1); } break; case NCP_IPV6CP: if (!iface->ipv6_up) { iface->ipv6_up=1; IfaceIpv6IfaceUp(b, 1); } break; case NCP_NONE: /* Manual call by 'open iface' */ if (Enabled(&b->conf.options, BUND_CONF_IPCP) && iface->dod && !iface->ip_up) { iface->ip_up=1; IfaceIpIfaceUp(b, 0); } if (Enabled(&b->conf.options, BUND_CONF_IPV6CP) && iface->dod && !iface->ipv6_up) { iface->ipv6_up=1; IfaceIpv6IfaceUp(b, 0); } break; } } void BundNcpsLeave(Bund b, int proto) { IfaceState iface = &b->iface; switch(proto) { case NCP_IPCP: if (iface->ip_up && !iface->dod) { iface->ip_up=0; IfaceIpIfaceDown(b); } break; case NCP_IPV6CP: if (iface->ipv6_up && !iface->dod) { iface->ipv6_up=0; IfaceIpv6IfaceDown(b); } break; case NCP_NONE: if (iface->ip_up && iface->dod) { iface->ip_up=0; IfaceIpIfaceDown(b); } if (iface->ipv6_up && iface->dod) { iface->ipv6_up=0; IfaceIpv6IfaceDown(b); } break; } if ((iface->up) && (!iface->ip_up) && (!iface->ipv6_up)) { iface->dod=0; iface->up=0; IfaceDown(b); if (iface->open) { iface->dod=1; iface->up=1; IfaceUp(b, 0); if (Enabled(&b->conf.options, BUND_CONF_IPCP)) { iface->ip_up=1; IfaceIpIfaceUp(b, 0); } if (Enabled(&b->conf.options, BUND_CONF_IPV6CP)) { iface->ipv6_up=1; IfaceIpv6IfaceUp(b, 0); } } } } /* * BundNcpsDown() */ static void BundNcpsDown(Bund b) { if (Enabled(&b->conf.options, BUND_CONF_IPCP)) IpcpDown(b); if (Enabled(&b->conf.options, BUND_CONF_IPV6CP)) Ipv6cpDown(b); if (Enabled(&b->conf.options, BUND_CONF_COMPRESSION)) CcpDown(b); if (Enabled(&b->conf.options, BUND_CONF_ENCRYPTION)) EcpDown(b); } /* * BundNcpsClose() */ void BundNcpsClose(Bund b) { if (Enabled(&b->conf.options, BUND_CONF_IPCP)) IpcpClose(b); if (Enabled(&b->conf.options, BUND_CONF_IPV6CP)) Ipv6cpClose(b); if (Enabled(&b->conf.options, BUND_CONF_COMPRESSION)) CcpClose(b); if (Enabled(&b->conf.options, BUND_CONF_ENCRYPTION)) EcpClose(b); } /* * BundReasses() * * Here we do a reassessment of things after a new link has been * added to or removed from the bundle. */ static void BundReasses(Bund b, int add) { BundBm const bm = &b->bm; /* Add or subtract link */ if (add) bm->n_up++; else bm->n_up--; /* Update system interface parameters */ BundUpdateParams(b); Log(LG_BUND, ("[%s] Bundle up: %d link%s, total bandwidth %d bps", b->name, bm->n_up, bm->n_up == 1 ? "" : "s", bm->total_bw)); } /* * BundUpdateParams() * * Recalculate interface MTU and bandwidth. */ void BundUpdateParams(Bund b) { BundBm const bm = &b->bm; int k, mtu, the_link = 0; /* Recalculate how much bandwidth we have */ for (bm->total_bw = k = 0; k < b->n_links; k++) { if (b->links[k]->lcp.phase == PHASE_NETWORK) { bm->total_bw += b->links[k]->bandwidth; the_link = k; } } if (bm->total_bw < BUND_MIN_TOT_BW) bm->total_bw = BUND_MIN_TOT_BW; /* Recalculate MTU corresponding to peer's MRU */ switch (bm->n_up) { case 0: mtu = NG_IFACE_MTU_DEFAULT; /* Reset to default settings */ break; case 1: if (!b->multilink) { /* If no multilink, use peer MRU */ mtu = MIN(b->links[the_link]->lcp.peer_mru, b->links[the_link]->phys->type->mtu); break; } /* FALLTHROUGH */ default: /* We fragment everything, use bundle MRRU */ mtu = b->mp.peer_mrru; break; } /* Subtract to make room for various frame-bloating protocols */ if (bm->n_up > 0) { if (Enabled(&b->conf.options, BUND_CONF_COMPRESSION)) mtu = CcpSubtractBloat(b, mtu); if (Enabled(&b->conf.options, BUND_CONF_ENCRYPTION)) mtu = EcpSubtractBloat(b, mtu); } /* Update interface MTU */ IfaceSetMTU(b, mtu); } /* * BundCommand() * * Show list of all bundles or set bundle */ int BundCommand(Context ctx, int ac, char *av[], void *arg) { Bund sb; int k; switch (ac) { case 0: #define BUND_FMT "\t%-15s" Printf("Defined bundles:\r\n"); Printf(BUND_FMT "Links\r\n", "Bundle"); Printf(BUND_FMT "-----\r\n", "------"); for (k = 0; k < gNumBundles; k++) if ((sb = gBundles[k]) != NULL) { Printf(BUND_FMT, sb->name); BundShowLinks(ctx, sb); } break; case 1: /* Change bundle, and link also if needed */ if ((sb = BundFind(av[0])) != NULL) { ctx->bund = sb; if (ctx->lnk == NULL || ctx->lnk->bund != ctx->bund) { ctx->lnk = ctx->bund->links[0]; } ctx->phys = ctx->lnk->phys; ctx->rep = NULL; } else { Printf("Bundle \"%s\" not defined.\r\n", av[0]); ctx->lnk = NULL; ctx->bund = NULL; ctx->phys = NULL; ctx->rep = NULL; } break; default: return(-1); } return(0); } /* * MSessionCommand() */ int MSessionCommand(Context ctx, int ac, char *av[], void *arg) { int k; if (ac != 1) return(-1); /* Find link */ for (k = 0; k < gNumBundles && (gBundles[k] == NULL || strcmp(gBundles[k]->msession_id, av[0])); k++); if (k == gNumBundles) { Printf("MultySession \"%s\" is not found\r\n", av[0]); /* Change default link and bundle */ ctx->lnk = NULL; ctx->bund = NULL; ctx->phys = NULL; ctx->rep = NULL; } else { /* Change default link and bundle */ ctx->bund = gBundles[k]; if (ctx->lnk == NULL || ctx->lnk->bund != ctx->bund) { ctx->lnk = ctx->bund->links[0]; } ctx->phys = ctx->lnk->phys; ctx->rep = NULL; } return(0); } /* * BundCreateCmd() * * Create a new bundle. If some of the links can't be added, * then we just use the ones that could. */ int BundCreateCmd(Context ctx, int ac, char *av[], void *arg) { Bund b; Link new_link; char *reqIface = NULL; u_char tee = 0; u_char netflow_in = 0; u_char netflow_out = 0; u_char nat = 0; int k; /* Args */ if (ac < 2) return(-1); if (ac > 0 && av[0][0] == '-') { optreset = 1; optind = 0; while ((k = getopt(ac, av, "nNati:")) != -1) { switch (k) { case 'i': reqIface = optarg; break; case 't': tee = 1; break; case 'n': #ifdef USE_NG_NETFLOW netflow_in = 1; #endif break; case 'N': #ifdef USE_NG_NETFLOW netflow_out = 1; #endif break; case 'a': #ifdef USE_NG_NAT nat = 1; #endif break; default: return (-1); } } ac -= optind; av += optind; } #if NG_NODESIZ>=32 if (strlen(av[0])>16) { #else if (strlen(av[0])>6) { #endif Log(LG_ERR, ("bundle name \"%s\" is too long", av[0])); return(0); } /* See if bundle name already taken */ if ((b = BundFind(av[0])) != NULL) { Log(LG_ERR, ("bundle \"%s\" already exists", av[0])); return(0); } /* Create a new bundle structure */ b = Malloc(MB_BUND, sizeof(*b)); snprintf(b->name, sizeof(b->name), "%s", av[0]); b->csock = b->dsock = -1; /* Setup netgraph stuff */ if (BundNgInit(b, reqIface) < 0) { Log(LG_ERR, ("[%s] netgraph initialization failed", b->name)); Freee(MB_BUND, b); return(0); } /* Create each link and add it to the bundle */ b->links = Malloc(MB_LINK, (ac - 1) * sizeof(*b->links)); for (k = 1; k < ac; k++) { #if NG_NODESIZ>=32 if (strlen(av[k])>16) { #else if (strlen(av[k])>6) { #endif Log(LG_ERR, ("link name \"%s\" is too long", av[k])); BundShutdown(b); return(0); } if ((new_link = LinkNew(av[k], b, b->n_links)) == NULL) Log(LG_ERR, ("[%s] addition of link \"%s\" failed", av[0], av[k])); else { b->links[b->n_links] = new_link; b->n_links++; } } /* We need at least one link in the bundle */ if (b->n_links == 0) { Log(LG_ERR, ("bundle \"%s\" creation failed: no links", av[0])); BundShutdown(b); return(0); } /* Add bundle to the list of bundles and make it the current active bundle */ for (k = 0; k < gNumBundles && gBundles[k] != NULL; k++); if (k == gNumBundles) /* add a new bundle pointer */ LengthenArray(&gBundles, sizeof(*gBundles), &gNumBundles, MB_BUND); b->id = k; gBundles[k] = b; /* Init interface stuff */ IfaceInit(b); if (tee) Enable(&b->iface.options, IFACE_CONF_TEE); if (nat) Enable(&b->iface.options, IFACE_CONF_NAT); if (netflow_in) Enable(&b->iface.options, IFACE_CONF_NETFLOW_IN); if (netflow_out) Enable(&b->iface.options, IFACE_CONF_NETFLOW_OUT); /* Get message channel */ b->msgs = MsgRegister(BundMsg); /* Initialize bundle configuration */ b->conf.mrru = MP_DEFAULT_MRRU; b->conf.retry_timeout = BUND_DEFAULT_RETRY; b->conf.bm_S = BUND_BM_DFL_S; b->conf.bm_Hi = BUND_BM_DFL_Hi; b->conf.bm_Lo = BUND_BM_DFL_Lo; b->conf.bm_Mc = BUND_BM_DFL_Mc; b->conf.bm_Md = BUND_BM_DFL_Md; if (b->n_links > 1) Enable(&b->conf.options, BUND_CONF_MULTILINK); Enable(&b->conf.options, BUND_CONF_SHORTSEQ); Accept(&b->conf.options, BUND_CONF_SHORTSEQ); Enable(&b->conf.options, BUND_CONF_IPCP); Disable(&b->conf.options, BUND_CONF_IPV6CP); Disable(&b->conf.options, BUND_CONF_BWMANAGE); Disable(&b->conf.options, BUND_CONF_COMPRESSION); Disable(&b->conf.options, BUND_CONF_ENCRYPTION); Disable(&b->conf.options, BUND_CONF_CRYPT_REQD); Enable(&b->conf.options, BUND_CONF_NORETRY); /* Init NCP's */ IpcpInit(b); Ipv6cpInit(b); CcpInit(b); EcpInit(b); ctx->bund = b; ctx->lnk = b->links[0]; ctx->phys = b->links[0]->phys; ctx->rep = NULL; /* Done */ return(0); } /* * BundShutdown() * * Shutdown the netgraph stuff associated with bundle */ void BundShutdown(Bund b) { Link l; int k; for (k = 0; k < b->n_links; k++) { l = b->links[k]; if (l) LinkShutdown(l); } Freee(MB_LINK, b->links); BundNgShutdown(b, 1, 1); gBundles[b->id] = NULL; Freee(MB_BUND, b); } /* * BundStat() * * Show state of a bundle */ int BundStat(Context ctx, int ac, char *av[], void *arg) { Bund sb; int k, bw, tbw, nup; char buf[64]; /* Find bundle they're talking about */ switch (ac) { case 0: sb = ctx->bund; break; case 1: if ((sb = BundFind(av[0])) == NULL) { Printf("Bundle \"%s\" not defined.\r\n", av[0]); return(0); } break; default: return(-1); } /* Show stuff about the bundle */ for (tbw = bw = nup = k = 0; k < sb->n_links; k++) { if (sb->links[k]->lcp.phase == PHASE_NETWORK) { nup++; bw += sb->links[k]->bandwidth; } tbw += sb->links[k]->bandwidth; } Printf("Bundle %s:\r\n", sb->name); Printf("\tLinks : "); BundShowLinks(ctx, sb); Printf("\tStatus : %s\r\n", sb->open ? "OPEN" : "CLOSED"); Printf("\tM-Session-Id : %s\r\n", sb->msession_id); Printf("\tTotal bandwidth: %u bits/sec\r\n", tbw); Printf("\tAvail bandwidth: %u bits/sec\r\n", bw); Printf("\tPeer authname : \"%s\"\r\n", sb->params.authname); Printf("\tPeer discrim. : %s\r\n", MpDiscrimText(&sb->peer_discrim, buf, sizeof(buf))); /* Show configuration */ Printf("Configuration:\r\n"); Printf("\tMy MRRU : %d bytes\r\n", sb->conf.mrru); Printf("\tRetry timeout : %d seconds\r\n", sb->conf.retry_timeout); Printf("\tBW-manage:\r\n"); Printf("\t Period : %d seconds\r\n", sb->conf.bm_S); Printf("\t Low mark : %d%%\r\n", sb->conf.bm_Lo); Printf("\t High mark : %d%%\r\n", sb->conf.bm_Hi); Printf("\t Min conn : %d seconds\r\n", sb->conf.bm_Mc); Printf("\t Min disc : %d seconds\r\n", sb->conf.bm_Md); Printf("Bundle level options:\r\n"); OptStat(ctx, &sb->conf.options, gConfList); /* Show peer info */ if (sb->bm.n_up > 0) { Printf("Multilink PPP:\r\n"); Printf("\tStatus : %s\r\n", sb->multilink ? "Active" : "Inactive\r\n"); if (sb->multilink) { Printf("\tPeer auth name : \"%s\"\r\n", sb->params.authname); Printf("\tPeer discrimin.: %s\r\n", MpDiscrimText(&sb->peer_discrim, buf, sizeof(buf))); } } /* Show stats */ BundUpdateStats(ctx->bund); Printf("Traffic stats:\r\n"); Printf("\tOctets input : %llu\r\n", (unsigned long long)ctx->bund->stats.recvOctets); Printf("\tFrames input : %llu\r\n", (unsigned long long)ctx->bund->stats.recvFrames); Printf("\tOctets output : %llu\r\n", (unsigned long long)ctx->bund->stats.xmitOctets); Printf("\tFrames output : %llu\r\n", (unsigned long long)ctx->bund->stats.xmitFrames); Printf("\tBad protocols : %llu\r\n", (unsigned long long)ctx->bund->stats.badProtos); Printf("\tRunts : %llu\r\n", (unsigned long long)ctx->bund->stats.runts); Printf("\tDup fragments : %llu\r\n", (unsigned long long)ctx->bund->stats.dupFragments); Printf("\tDrop fragments : %llu\r\n", (unsigned long long)ctx->bund->stats.dropFragments); return(0); } /* * BundUpdateStats() */ void BundUpdateStats(Bund b) { #ifndef NG_PPP_STATS64 struct ng_ppp_link_stat stats; #endif int l = NG_PPP_BUNDLE_LINKNUM; #if (__FreeBSD_version < 602104 || (__FreeBSD_version >= 700000 && __FreeBSD_version < 700029)) /* Workaround for broken ng_ppp bundle stats */ if (!b->multilink) l = 0; #endif #ifndef NG_PPP_STATS64 if (NgFuncGetStats(b, l, &stats) != -1) { b->stats.xmitFrames += abs(stats.xmitFrames - b->oldStats.xmitFrames); b->stats.xmitOctets += abs(stats.xmitOctets - b->oldStats.xmitOctets); b->stats.recvFrames += abs(stats.recvFrames - b->oldStats.recvFrames); b->stats.recvOctets += abs(stats.recvOctets - b->oldStats.recvOctets); b->stats.badProtos += abs(stats.badProtos - b->oldStats.badProtos); b->stats.runts += abs(stats.runts - b->oldStats.runts); b->stats.dupFragments += abs(stats.dupFragments - b->oldStats.dupFragments); b->stats.dropFragments += abs(stats.dropFragments - b->oldStats.dropFragments); } b->oldStats = stats; #else NgFuncGetStats64(b, l, &b->stats); #endif } /* * BundUpdateStatsTimer() */ void BundUpdateStatsTimer(void *cookie) { Bund b = (Bund)cookie; int k; BundUpdateStats(b); for (k = 0; k < b->n_links; k++) { if (b->links[k]->joined_bund) LinkUpdateStats(b->links[k]); } } /* * BundResetStats() */ void BundResetStats(Bund b) { NgFuncClrStats(b, NG_PPP_BUNDLE_LINKNUM); memset(&b->stats, 0, sizeof(b->stats)); #ifndef NG_PPP_STATS64 memset(&b->oldStats, 0, sizeof(b->oldStats)); #endif } /* * BundShowLinks() */ static void BundShowLinks(Context ctx, Bund sb) { int j; for (j = 0; j < sb->n_links; j++) { Printf("%s", sb->links[j]->name); if (!sb->links[j]->phys->type) Printf("[no type] "); else Printf("[%s/%s] ", FsmStateName(sb->links[j]->lcp.fsm.state), gPhysStateNames[sb->links[j]->phys->state]); } Printf("\r\n"); } /* * BundFind() * * Find a bundle structure */ static Bund BundFind(char *name) { int k; for (k = 0; k < gNumBundles && (!gBundles[k] || strcmp(gBundles[k]->name, name)); k++); return((k < gNumBundles) ? gBundles[k] : NULL); } /* * BundBmStart() * * Start bandwidth management timer */ static void BundBmStart(Bund b) { int k; /* Reset bandwidth management stats */ for (k = 0; k < b->n_links; k++) { memset(&b->links[k]->bm.traffic, 0, sizeof(b->links[k]->bm.traffic)); memset(&b->links[k]->bm.wasUp, 0, sizeof(b->links[k]->bm.wasUp)); memset(&b->links[k]->bm.idleStats, 0, sizeof(b->links[k]->bm.idleStats)); } /* Start bandwidth management timer */ TimerStop(&b->bm.bmTimer); if (Enabled(&b->conf.options, BUND_CONF_BWMANAGE)) { TimerInit(&b->bm.bmTimer, "BundBm", b->conf.bm_S * SECONDS / LINK_BM_N, BundBmTimeout, b); TimerStart(&b->bm.bmTimer); } } /* * BundBmStop() */ static void BundBmStop(Bund b) { TimerStop(&b->bm.bmTimer); } /* * BundBmTimeout() * * Do a bandwidth management update */ static void BundBmTimeout(void *arg) { Bund b = (Bund)arg; const time_t now = time(NULL); u_int availTotal; u_int inUtilTotal = 0, outUtilTotal = 0; u_int inBitsTotal, outBitsTotal; u_int inUtil[LINK_BM_N]; /* Incoming % utilization */ u_int outUtil[LINK_BM_N]; /* Outgoing % utilization */ int j, k; /* Shift and update stats */ for (k = 0; k < b->n_links; k++) { Link const l = b->links[k]; /* Shift stats */ memmove(&l->bm.wasUp[1], &l->bm.wasUp[0], (LINK_BM_N - 1) * sizeof(l->bm.wasUp[0])); l->bm.wasUp[0] = (l->lcp.fsm.state == ST_OPENED); memmove(&l->bm.traffic[0][1], &l->bm.traffic[0][0], (LINK_BM_N - 1) * sizeof(l->bm.traffic[0][0])); memmove(&l->bm.traffic[1][1], &l->bm.traffic[1][0], (LINK_BM_N - 1) * sizeof(l->bm.traffic[1][0])); if (!l->bm.wasUp[0]) { l->bm.traffic[0][0] = 0; l->bm.traffic[1][0] = 0; } else { struct ng_ppp_link_stat oldStats; /* Get updated link traffic statistics */ oldStats = l->bm.idleStats; NgFuncGetStats(l->bund, l->bundleIndex, &l->bm.idleStats); l->bm.traffic[0][0] = l->bm.idleStats.recvOctets - oldStats.recvOctets; l->bm.traffic[1][0] = l->bm.idleStats.xmitOctets - oldStats.xmitOctets; } } /* Compute utilizations */ memset(&inUtil, 0, sizeof(inUtil)); memset(&outUtil, 0, sizeof(outUtil)); for (availTotal = inBitsTotal = outBitsTotal = j = 0; j < LINK_BM_N; j++) { u_int avail, inBits, outBits; /* Sum up over all links */ for (avail = inBits = outBits = k = 0; k < b->n_links; k++) { Link const l = b->links[k]; if (l->bm.wasUp[j]) { avail += (l->bandwidth * b->conf.bm_S) / LINK_BM_N; inBits += l->bm.traffic[0][j] * 8; outBits += l->bm.traffic[1][j] * 8; } } availTotal += avail; inBitsTotal += inBits; outBitsTotal += outBits; /* Compute bandwidth utilizations as percentages */ if (avail != 0) { inUtil[j] = ((float) inBits / avail) * 100; outUtil[j] = ((float) outBits / avail) * 100; } } /* Compute total averaged utilization */ if (availTotal != 0) { inUtilTotal = ((float) inBitsTotal / availTotal) * 100; outUtilTotal = ((float) outBitsTotal / availTotal) * 100; } #ifdef DEBUG_BOD { char ins[100], outs[100]; snprintf(ins, sizeof(ins), ">>Link status: "); for (j = 0; j < LINK_BM_N; j++) { for (k = 0; k < b->n_links; k++) { Link const l = b->links[k]; snprintf(ins + strlen(ins), sizeof(ins) - strlen(ins), l->bm.wasUp[LINK_BM_N - 1 - j] ? "Up" : "Dn"); } snprintf(ins + strlen(ins), sizeof(ins) - strlen(ins), " "); } Log(LG_BUND, ("%s", ins)); snprintf(ins, sizeof(ins), " IN util: total %3u%% ", inUtilTotal); snprintf(outs, sizeof(outs), "OUT util: total %3u%% ", outUtilTotal); for (j = 0; j < LINK_BM_N; j++) { snprintf(ins + strlen(ins), sizeof(ins) - strlen(ins), " %3u%%", inUtil[LINK_BM_N - 1 - j]); snprintf(outs + strlen(outs), sizeof(outs) - strlen(outs), " %3u%%", outUtil[LINK_BM_N - 1 - j]); } Log(LG_BUND, (" %s", ins)); Log(LG_BUND, (" %s", outs)); } #endif /* See if it's time to bring up another link */ if (now - b->bm.last_open >= b->conf.bm_Mc && (inUtilTotal >= b->conf.bm_Hi || outUtilTotal >= b->conf.bm_Hi) && b->bm.n_open < b->n_links) { k = 0; while (k < b->n_links && OPEN_STATE(b->links[k]->lcp.fsm.state)) k++; assert(k < b->n_links); Log(LG_BUND, ("[%s] opening link %s due to increased demand", b->name, b->links[k]->name)); b->bm.last_open = now; RecordLinkUpDownReason(NULL, b->links[k], 1, STR_PORT_NEEDED, NULL); BundOpenLink(b->links[k]); } /* See if it's time to bring down a link */ if (now - b->bm.last_close >= b->conf.bm_Md && (inUtilTotal < b->conf.bm_Lo && outUtilTotal < b->conf.bm_Lo) && b->bm.n_up > 1) { k = b->n_links - 1; while (k >= 0 && !OPEN_STATE(b->links[k]->lcp.fsm.state)) k--; assert(k >= 0); Log(LG_BUND, ("[%s] closing link %s due to reduced demand", b->name, b->links[k]->name)); b->bm.last_close = now; RecordLinkUpDownReason(NULL, b->links[k], 0, STR_PORT_UNNEEDED, NULL); BundCloseLink(b->links[k]); } /* Restart timer */ TimerStart(&b->bm.bmTimer); } /* * BundNgInit() * * Setup the initial PPP netgraph framework. Initializes these fields * in the supplied bundle structure: * * iface.ifname - Interface name * csock - Control socket for socket netgraph node * dsock - Data socket for socket netgraph node * * Returns -1 if error. */ static int BundNgInit(Bund b, const char *reqIface) { union { u_char buf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)]; struct ng_mesg reply; } u; struct nodeinfo *const ni = (struct nodeinfo *)(void *)u.reply.data; struct ngm_mkpeer mp; struct ngm_name nm; int newIface = 0; int newPpp = 0; /* Create a netgraph socket node */ if (NgMkSockNode(NULL, &b->csock, &b->dsock) < 0) { Log(LG_ERR, ("[%s] can't create %s node: %s", b->name, NG_SOCKET_NODE_TYPE, strerror(errno))); return(-1); } (void) fcntl(b->csock, F_SETFD, 1); (void) fcntl(b->dsock, F_SETFD, 1); #if NG_NODESIZ>=32 /* Give it a name */ snprintf(nm.name, sizeof(nm.name), "mpd%d-%s-so", gPid, b->name); if (NgSendMsg(b->csock, ".", NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) { Log(LG_ERR, ("[%s] can't name %s node: %s", b->name, NG_SOCKET_NODE_TYPE, strerror(errno))); goto fail; } #endif /* Create new iface node if necessary, else find the one specified */ if (reqIface != NULL) { switch (NgFuncIfaceExists(b, reqIface, b->iface.ifname, sizeof(b->iface.ifname))) { case -1: /* not a netgraph interface */ Log(LG_ERR, ("[%s] interface \"%s\" is not a netgraph interface", b->name, reqIface)); goto fail; break; case 0: /* interface does not exist */ if (NgFuncCreateIface(b, reqIface, b->iface.ifname, sizeof(b->iface.ifname)) < 0) { Log(LG_ERR, ("[%s] can't create interface \"%s\"", b->name, reqIface)); goto fail; } break; case 1: /* interface exists */ break; default: assert(0); } } else { if (NgFuncCreateIface(b, NULL, b->iface.ifname, sizeof(b->iface.ifname)) < 0) { Log(LG_ERR, ("[%s] can't create netgraph interface", b->name)); goto fail; } newIface = 1; } b->iface.ifindex = if_nametoindex(b->iface.ifname); /* Create new PPP node */ snprintf(mp.type, sizeof(mp.type), "%s", NG_PPP_NODE_TYPE); snprintf(mp.ourhook, sizeof(mp.ourhook), "%s", MPD_HOOK_PPP); snprintf(mp.peerhook, sizeof(mp.peerhook), "%s", NG_PPP_HOOK_BYPASS); if (NgSendMsg(b->csock, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) { Log(LG_ERR, ("[%s] can't create %s node at \"%s\"->\"%s\": %s", b->name, mp.type, ".", mp.ourhook, strerror(errno))); goto fail; } newPpp = 1; /* Give it a name */ snprintf(nm.name, sizeof(nm.name), "mpd%d-%s", gPid, b->name); if (NgSendMsg(b->csock, MPD_HOOK_PPP, NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) { Log(LG_ERR, ("[%s] can't name %s node \"%s\": %s", b->name, NG_PPP_NODE_TYPE, MPD_HOOK_PPP, strerror(errno))); goto fail; } /* Get PPP node ID */ if (NgSendMsg(b->csock, MPD_HOOK_PPP, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) { Log(LG_ERR, ("[%s] ppp nodeinfo: %s", b->name, strerror(errno))); goto fail; } if (NgRecvMsg(b->csock, &u.reply, sizeof(u), NULL) < 0) { Log(LG_ERR, ("[%s] node \"%s\" reply: %s", b->name, MPD_HOOK_PPP, strerror(errno))); goto fail; } b->nodeID = ni->id; /* Listen for happenings on our node */ EventRegister(&b->dataEvent, EVENT_READ, b->dsock, EVENT_RECURRING, BundNgDataEvent, b); /* Control events used only by CCP so Register events there */ /* OK */ return(0); fail: BundNgShutdown(b, newIface, newPpp); return(-1); } /* * NgFuncShutdown() */ void BundNgShutdown(Bund b, int iface, int ppp) { char path[NG_PATHLEN + 1]; if (iface) { snprintf(path, sizeof(path), "%s:", b->iface.ifname); NgFuncShutdownNode(b->csock, b->name, path); } if (ppp) { NgFuncShutdownNode(b->csock, b->name, MPD_HOOK_PPP); } close(b->csock); b->csock = -1; EventUnRegister(&b->ctrlEvent); close(b->dsock); b->dsock = -1; EventUnRegister(&b->dataEvent); } /* * BundNgDataEvent() */ static void BundNgDataEvent(int type, void *cookie) { Bund b = (Bund)cookie; u_char buf[8192]; struct sockaddr_ng naddr; int nread; socklen_t nsize = sizeof(naddr); Mbuf nbp; /* Read data */ if ((nread = recvfrom(b->dsock, buf, sizeof(buf), 0, (struct sockaddr *)&naddr, &nsize)) < 0) { if (errno == EAGAIN) return; Log(LG_BUND, ("[%s] socket read: %s", b->name, strerror(errno))); DoExit(EX_ERRDEAD); } /* A PPP frame from the bypass hook? */ if (strcmp(naddr.sg_data, MPD_HOOK_PPP) == 0) { u_int16_t linkNum, proto; /* Extract link number and protocol */ memcpy(&linkNum, buf, 2); linkNum = ntohs(linkNum); memcpy(&proto, buf + 2, 2); proto = ntohs(proto); /* Debugging */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd bypass frame link=%d proto=0x%04x", b->name, (int16_t)linkNum, proto); /* Set link */ assert(linkNum == NG_PPP_BUNDLE_LINKNUM || linkNum < b->n_links); /* Input frame */ InputFrame(b, linkNum, proto, mbufise(MB_FRAME_IN, buf + 4, nread - 4)); return; } /* A snooped, outgoing IP frame? */ if (strcmp(naddr.sg_data, MPD_HOOK_DEMAND_TAP) == 0) { /* Debugging */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd IP frame on demand/mssfix-in hook", b->name); IfaceListenInput(b, PROTO_IP, mbufise(MB_FRAME_IN, buf, nread)); return; } #ifndef USE_NG_TCPMSS /* A snooped, outgoing TCP SYN frame? */ if (strcmp(naddr.sg_data, MPD_HOOK_TCPMSS_OUT) == 0) { /* Debugging */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd IP frame on mssfix-out hook", b->name); nbp = mbufise(MB_FRAME_IN, buf, nread); IfaceCorrectMSS(nbp, MAXMSS(b->iface.mtu)); NgFuncWriteFrame(b, MPD_HOOK_TCPMSS_IN, nbp); return; } /* A snooped, incoming TCP SYN frame? */ if (strcmp(naddr.sg_data, MPD_HOOK_TCPMSS_IN) == 0) { /* Debugging */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd IP frame on mssfix-in hook", b->name); nbp = mbufise(MB_FRAME_IN, buf, nread); IfaceCorrectMSS(nbp, MAXMSS(b->iface.mtu)); NgFuncWriteFrame(b, MPD_HOOK_TCPMSS_OUT, nbp); return; } #endif /* Packet requiring compression */ if (strcmp(naddr.sg_data, NG_PPP_HOOK_COMPRESS) == 0) { /* Debugging */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd frame on %s hook", b->name, NG_PPP_HOOK_COMPRESS); nbp = CcpDataOutput(b, mbufise(MB_COMP, buf, nread)); if (nbp) NgFuncWriteFrame(b, NG_PPP_HOOK_COMPRESS, nbp); return; } /* Packet requiring decompression */ if (strcmp(naddr.sg_data, NG_PPP_HOOK_DECOMPRESS) == 0) { /* Debugging */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd frame on %s hook", b->name, NG_PPP_HOOK_DECOMPRESS); nbp = CcpDataInput(b, mbufise(MB_COMP, buf, nread)); if (nbp) NgFuncWriteFrame(b, NG_PPP_HOOK_DECOMPRESS, nbp); return; } /* Packet requiring encryption */ if (strcmp(naddr.sg_data, NG_PPP_HOOK_ENCRYPT) == 0) { /* Debugging */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd frame on %s hook", b->name, NG_PPP_HOOK_ENCRYPT); nbp = EcpDataOutput(b, mbufise(MB_CRYPT, buf, nread)); if (nbp) NgFuncWriteFrame(b, NG_PPP_HOOK_ENCRYPT, nbp); return; } /* Packet requiring decryption */ if (strcmp(naddr.sg_data, NG_PPP_HOOK_DECRYPT) == 0) { /* Debugging */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd frame on %s hook", b->name, NG_PPP_HOOK_DECRYPT); nbp = EcpDataInput(b, mbufise(MB_CRYPT, buf, nread)); if (nbp) NgFuncWriteFrame(b, NG_PPP_HOOK_DECRYPT, nbp); return; } /* Unknown hook! */ LogDumpBuf(LG_FRAME, buf, nread, "[%s] rec'd data on unknown hook \"%s\"", b->name, naddr.sg_data); DoExit(EX_ERRDEAD); } /* * BundNgCtrlEvent() * */ void BundNgCtrlEvent(int type, void *cookie) { Bund b = (Bund)cookie; union { u_char buf[8192]; struct ng_mesg msg; } u; char raddr[NG_PATHLEN + 1]; int len; /* Read message */ if ((len = NgRecvMsg(b->csock, &u.msg, sizeof(u), raddr)) < 0) { Log(LG_ERR, ("[%s] can't read unexpected message: %s", b->name, strerror(errno))); return; } /* Examine message */ switch (u.msg.header.typecookie) { case NGM_MPPC_COOKIE: #ifdef USE_NG_DEFLATE case NGM_DEFLATE_COOKIE: #endif #ifdef USE_NG_PRED1 case NGM_PRED1_COOKIE: #endif CcpRecvMsg(b, &u.msg, len); return; default: break; } /* Unknown message */ Log(LG_ERR, ("[%s] rec'd unknown ctrl message, cookie=%d cmd=%d", b->name, u.msg.header.typecookie, u.msg.header.cmd)); } /* * BundSetCommand() */ static int BundSetCommand(Context ctx, int ac, char *av[], void *arg) { Bund b = ctx->bund; if (ac == 0) return(-1); switch ((intptr_t)arg) { case SET_PERIOD: b->conf.bm_S = atoi(*av); break; case SET_LOW_WATER: b->conf.bm_Lo = atoi(*av); break; case SET_HIGH_WATER: b->conf.bm_Hi = atoi(*av); break; case SET_MIN_CONNECT: b->conf.bm_Mc = atoi(*av); break; case SET_MIN_DISCONNECT: b->conf.bm_Md = atoi(*av); break; case SET_RETRY: b->conf.retry_timeout = atoi(*av); if (b->conf.retry_timeout < 1 || b->conf.retry_timeout > 10) b->conf.retry_timeout = BUND_DEFAULT_RETRY; break; case SET_ACCEPT: AcceptCommand(ac, av, &b->conf.options, gConfList); break; case SET_DENY: DenyCommand(ac, av, &b->conf.options, gConfList); break; case SET_ENABLE: EnableCommand(ac, av, &b->conf.options, gConfList); break; case SET_DISABLE: DisableCommand(ac, av, &b->conf.options, gConfList); if (!Enabled(&b->conf.options, BUND_CONF_MULTILINK) && b->n_links > 1) { Log(LG_ERR, ("[%s] multilink option required for %d links", b->name, b->n_links)); Enable(&b->conf.options, BUND_CONF_MULTILINK); } break; case SET_YES: YesCommand(ac, av, &b->conf.options, gConfList); break; case SET_NO: NoCommand(ac, av, &b->conf.options, gConfList); break; default: assert(0); } return(0); }