/* * nat.c * * Written by Alexander Motin * Rewritten by Dmitry Luhtionov */ #include "ppp.h" #include "nat.h" #include "iface.h" #include "netgraph.h" #ifdef NG_NAT_DESC_LENGTH #include "ngfunc.h" #endif #include "util.h" /* * DEFINITIONS */ /* Set menu options */ enum { SET_ADDR, SET_TARGET, SET_ENABLE, SET_DISABLE, SET_REDIRECT_PORT, SET_REDIRECT_ADDR, SET_REDIRECT_PROTO, UNSET_REDIRECT_PORT, UNSET_REDIRECT_ADDR, UNSET_REDIRECT_PROTO }; static int NatSetCommand(Context ctx, int ac, const char *const av[], const void *arg); /* * GLOBAL VARIABLES */ #ifdef NG_NAT_DESC_LENGTH const struct cmdtab NatUnSetCmds[] = { { "red-port {proto} {alias_addr} {alias_port} {local_addr} {local_port} [{remote_addr} {remote_port}]", "Redirect port", NatSetCommand, AdmitBund, 2, (void *) UNSET_REDIRECT_PORT }, { "red-addr {alias_addr} {local_addr}", "Redirect address", NatSetCommand, AdmitBund, 2, (void *) UNSET_REDIRECT_ADDR }, { "red-proto {proto} {alias-addr} {local_addr} [{remote-addr}]", "Redirect protocol", NatSetCommand, AdmitBund, 2, (void *) UNSET_REDIRECT_PROTO }, { NULL, NULL, NULL, NULL, 0, NULL }, }; #endif const struct cmdtab NatSetCmds[] = { { "address {addr}", "Set alias address", NatSetCommand, AdmitBund, 2, (void *) SET_ADDR }, { "target {addr}", "Set target address", NatSetCommand, AdmitBund, 2, (void *) SET_TARGET }, #ifdef NG_NAT_DESC_LENGTH { "red-port {proto} {alias_addr} {alias_port} {local_addr} {local_port} [{remote_addr} {remote_port}]", "Redirect port", NatSetCommand, AdmitBund, 2, (void *) SET_REDIRECT_PORT }, { "red-addr {alias_addr} {local_addr}", "Redirect address", NatSetCommand, AdmitBund, 2, (void *) SET_REDIRECT_ADDR }, { "red-proto {proto} {alias-addr} {local_addr} [{remote-addr}]", "Redirect protocol", NatSetCommand, AdmitBund, 2, (void *) SET_REDIRECT_PROTO }, #endif { "enable [opt ...]", "Enable option", NatSetCommand, AdmitBund, 2, (void *) SET_ENABLE }, { "disable [opt ...]", "Disable option", NatSetCommand, AdmitBund, 2, (void *) SET_DISABLE }, { NULL, NULL, NULL, NULL, 0, NULL }, }; /* * INTERNAL VARIABLES */ static const struct confinfo gConfList[] = { { 0, NAT_CONF_LOG, "log" }, { 0, NAT_CONF_INCOMING, "incoming" }, { 0, NAT_CONF_SAME_PORTS, "same-ports" }, { 0, NAT_CONF_UNREG_ONLY, "unreg-only" }, { 0, 0, NULL }, }; /* * NatInit() */ void NatInit(Bund b) { NatState const nat = &b->iface.nat; /* Default configuration */ u_addrclear(&nat->alias_addr); u_addrclear(&nat->target_addr); Disable(&nat->options, NAT_CONF_LOG); Enable(&nat->options, NAT_CONF_INCOMING); Enable(&nat->options, NAT_CONF_SAME_PORTS); Disable(&nat->options, NAT_CONF_UNREG_ONLY); #ifdef NG_NAT_DESC_LENGTH bzero(nat->nrpt, sizeof(nat->nrpt)); bzero(nat->nrpt_id, sizeof(nat->nrpt_id)); bzero(nat->nrad, sizeof(nat->nrad)); bzero(nat->nrad_id, sizeof(nat->nrad_id)); bzero(nat->nrpr, sizeof(nat->nrpr)); bzero(nat->nrpr_id, sizeof(nat->nrpr_id)); #endif } /* * NatSetCommand() */ static int NatSetCommand(Context ctx, int ac, const char *const av[], const void *arg) { NatState const nat = &ctx->bund->iface.nat; IfaceState const iface = &ctx->bund->iface; #ifdef NG_NAT_DESC_LENGTH char path[NG_PATHSIZ]; union { u_char buf[sizeof(struct ng_mesg) + sizeof(uint32_t)]; struct ng_mesg reply; } u; uint32_t *const nat_id = (uint32_t *)(void *)u.reply.data; snprintf(path, sizeof(path), "mpd%d-%s-nat:", gPid, (char *)&ctx->bund->name); #endif if (ac == 0) return(-1); switch ((intptr_t)arg) { case SET_TARGET: #ifndef NG_NAT_LOG Error("Target address setting is unsupported by current kernel"); #endif /* FALL */ case SET_ADDR: { struct u_addr addr; /* Parse */ if (ac != 1) return(-1); if (!ParseAddr(av[0], &addr, ALLOW_IPV4)) Error("bad IP address \"%s\"", av[0]); /* OK */ if ((intptr_t)arg == SET_ADDR) { nat->alias_addr = addr; } else { nat->target_addr = addr; } } break; #ifdef NG_NAT_DESC_LENGTH case SET_REDIRECT_PORT: case UNSET_REDIRECT_PORT: { struct protoent *proto; struct in_addr l_addr, a_addr, r_addr; int lp, ap, rp = 0; uint32_t k; /* Parse */ if (ac != 5 && ac != 7) return(-1); if ((proto = getprotobyname(av[0])) == 0) Error("bad PROTO name \"%s\"", av[0]); if (!inet_aton (av[1], &a_addr)) Error("bad alias IP address \"%s\"", av[1]); ap = atoi(av[2]); if (ap <= 0 || ap > 65535) Error("Incorrect alias port number \"%s\"", av[2]); if (!inet_aton (av[3], &l_addr)) Error("bad local IP address \"%s\"", av[3]); lp = atoi(av[4]); if (lp <= 0 || lp > 65535) Error("Incorrect local port number \"%s\"", av[4]); if (ac == 7) { if (!inet_aton (av[5], &r_addr)) Error("bad remote IP address \"%s\"", av[5]); rp = atoi(av[6]); if (rp <= 0 || rp > 65535) Error("Incorrect remote port number \"%s\"", av[6]); } /* OK */ if ((intptr_t)arg == SET_REDIRECT_PORT) { for (k=0;knrpt_id[k] == 0) { memcpy(&nat->nrpt[k].local_addr, &l_addr, sizeof(struct in_addr)); memcpy(&nat->nrpt[k].alias_addr, &a_addr, sizeof(struct in_addr)); nat->nrpt[k].local_port = lp; nat->nrpt[k].alias_port = ap; if (ac == 7) { memcpy(&nat->nrpt[k].remote_addr, &r_addr, sizeof(struct in_addr)); nat->nrpt[k].remote_port = rp; } nat->nrpt[k].proto = (uint8_t)proto->p_proto; snprintf(nat->nrpt[k].description, NG_NAT_DESC_LENGTH, "nat-port-%d", k); nat->nrpt_id[k] = -1; if (iface->up && iface->nat_up) { if (NgFuncSendQuery(path, NGM_NAT_COOKIE, NGM_NAT_REDIRECT_PORT, &nat->nrpt[k], sizeof(struct ng_nat_redirect_port), &u.reply, sizeof(u), NULL) == 0) nat->nrpt_id[k] = *nat_id; } break; } } if (k == NM_PORT) Error("max number of redirect-port \"%d\" reached", NM_PORT); } else { struct ng_nat_redirect_port tmp_rule; bzero(&tmp_rule, sizeof(struct ng_nat_redirect_port)); memcpy(&tmp_rule.local_addr, &l_addr, sizeof(struct in_addr)); memcpy(&tmp_rule.alias_addr, &a_addr, sizeof(struct in_addr)); tmp_rule.local_port = lp; tmp_rule.alias_port = ap; if (ac == 7) { memcpy(&tmp_rule.remote_addr, &r_addr, sizeof(struct in_addr)); tmp_rule.remote_port = rp; } tmp_rule.proto = (uint8_t)proto->p_proto; /* hack to fill misaligned space */ snprintf(tmp_rule.description, NG_NAT_DESC_LENGTH, "nat-port-0"); for (k=0;knrpt_id[k] != 0) && (memcmp(&tmp_rule, &nat->nrpt[k], sizeof(struct ng_nat_redirect_port)-NG_NAT_DESC_LENGTH) == 0)) { if (iface->up && iface->nat_up) { if (NgSendMsg(gLinksCsock, path, NGM_NAT_COOKIE, NGM_NAT_REDIRECT_DELETE, &k, sizeof(k)) < 0) { Perror("Can't delete nat rule"); break; } } nat->nrpt_id[k] = 0; bzero(&nat->nrpt[k], sizeof(struct ng_nat_redirect_port)); break; } } if (k == NM_PORT) Error("Rule not found"); } } break; case SET_REDIRECT_ADDR: case UNSET_REDIRECT_ADDR: { struct in_addr l_addr, a_addr; uint32_t k; /* Parse */ if (ac != 2) return(-1); if (!inet_aton (av[0], &a_addr)) Error("bad alias IP address \"%s\"", av[0]); if (!inet_aton (av[1], &l_addr)) Error("bad local IP address \"%s\"", av[1]); /* OK */ if ((intptr_t)arg == SET_REDIRECT_ADDR) { for (k=0;knrad_id[k] == 0) { memcpy(&nat->nrad[k].local_addr, &l_addr, sizeof(struct in_addr)); memcpy(&nat->nrad[k].alias_addr, &a_addr, sizeof(struct in_addr)); snprintf(nat->nrad[k].description, NG_NAT_DESC_LENGTH, "nat-addr-%d", k); nat->nrad_id[k] = -1; if (iface->up && iface->nat_up) { if (NgFuncSendQuery(path, NGM_NAT_COOKIE, NGM_NAT_REDIRECT_ADDR, &nat->nrad[k], sizeof(struct ng_nat_redirect_addr), &u.reply, sizeof(u), NULL) == 0) nat->nrad_id[k] = *nat_id; } break; } } if (k == NM_ADDR) Error("max number of redirect-addr \"%d\" reached", NM_ADDR); } else { struct ng_nat_redirect_addr tmp_rule; bzero(&tmp_rule, sizeof(struct ng_nat_redirect_addr)); memcpy(&tmp_rule.local_addr, &l_addr, sizeof(struct in_addr)); memcpy(&tmp_rule.alias_addr, &a_addr, sizeof(struct in_addr)); /* hack to fill misaligned space */ snprintf(tmp_rule.description, NG_NAT_DESC_LENGTH, "nat-addr-0"); for (k=0;knrad_id[k] != 0) && (memcmp(&tmp_rule, &nat->nrad[k], sizeof(struct ng_nat_redirect_addr)-NG_NAT_DESC_LENGTH) == 0)) { if (iface->up && iface->nat_up) { if (NgSendMsg(gLinksCsock, path, NGM_NAT_COOKIE, NGM_NAT_REDIRECT_DELETE, &k, sizeof(k)) < 0) { Perror("Can't delete nat rule"); break; } } nat->nrad_id[k] = 0; bzero(&nat->nrad[k], sizeof(struct ng_nat_redirect_addr)); break; } } if (k == NM_ADDR) Error("Rule not found"); } } break; case SET_REDIRECT_PROTO: case UNSET_REDIRECT_PROTO: { struct protoent *proto; struct in_addr l_addr, a_addr, r_addr; uint32_t k; /* Parse */ if (ac != 3 && ac != 4) return(-1); if ((proto = getprotobyname(av[0])) == 0) Error("bad PROTO name \"%s\"", av[0]); if (!inet_aton (av[1], &a_addr)) Error("bad alias IP address \"%s\"", av[1]); if (!inet_aton (av[2], &l_addr)) Error("bad local IP address \"%s\"", av[2]); if (ac == 4) { if (!inet_aton (av[3], &r_addr)) Error("bad remote IP address \"%s\"", av[3]); } /* OK */ if ((intptr_t)arg == SET_REDIRECT_PROTO) { for (k=0;knrpr_id[k] == 0) { memcpy(&nat->nrpr[k].local_addr, &l_addr, sizeof(struct in_addr)); memcpy(&nat->nrpr[k].alias_addr, &a_addr, sizeof(struct in_addr)); if (ac == 4) memcpy(&nat->nrpr[k].remote_addr, &r_addr, sizeof(struct in_addr)); nat->nrpr[k].proto = (uint8_t)proto->p_proto; snprintf(nat->nrpr[k].description, NG_NAT_DESC_LENGTH, "nat-proto-%d", k); nat->nrpr_id[k] = -1; if (iface->up && iface->nat_up) { if (NgFuncSendQuery(path, NGM_NAT_COOKIE, NGM_NAT_REDIRECT_PROTO, &nat->nrpr[k], sizeof(struct ng_nat_redirect_proto), &u.reply, sizeof(u), NULL) == 0) nat->nrpr_id[k] = *nat_id; } break; } } if (k == NM_PROTO) Error("max number of redirect-proto \"%d\" reached", NM_PROTO); } else { struct ng_nat_redirect_proto tmp_rule; bzero(&tmp_rule, sizeof(struct ng_nat_redirect_proto)); memcpy(&tmp_rule.local_addr, &l_addr, sizeof(struct in_addr)); memcpy(&tmp_rule.alias_addr, &a_addr, sizeof(struct in_addr)); if (ac == 4) { memcpy(&tmp_rule.remote_addr, &r_addr, sizeof(struct in_addr)); } tmp_rule.proto = (uint8_t)proto->p_proto; /* hack to fill misaligned space */ snprintf(tmp_rule.description, NG_NAT_DESC_LENGTH, "nat-proto-0"); for (k=0;knrpr_id[k] != 0) && (memcmp(&tmp_rule, &nat->nrpr[k], sizeof(struct ng_nat_redirect_proto)-NG_NAT_DESC_LENGTH) == 0)) { if (iface->up && iface->nat_up) { if (NgSendMsg(gLinksCsock, path, NGM_NAT_COOKIE, NGM_NAT_REDIRECT_DELETE, &k, sizeof(k)) < 0) { Perror("Can't delete nat rule"); break; } } nat->nrpr_id[k] = 0; bzero(&nat->nrpr[k], sizeof(struct ng_nat_redirect_proto)); break; } } if (k == NM_PROTO) Error("Rule not found"); } } break; #endif case SET_ENABLE: EnableCommand(ac, av, &nat->options, gConfList); break; case SET_DISABLE: DisableCommand(ac, av, &nat->options, gConfList); break; default: assert(0); } return(0); } /* * NatStat() */ int NatStat(Context ctx, int ac, const char *const av[], const void *arg) { NatState const nat = &ctx->bund->iface.nat; #ifdef NG_NAT_LIBALIAS_INFO IfaceState const iface = &ctx->bund->iface; union { u_char buf[sizeof(struct ng_mesg) + sizeof(struct ng_nat_libalias_info)]; struct ng_mesg reply; } u; struct ng_nat_libalias_info *const nli = \ (struct ng_nat_libalias_info *)(void *)u.reply.data; char path[NG_PATHSIZ]; #endif char buf[48]; int k; (void)ac; (void)av; (void)arg; Printf("NAT configuration:\r\n"); Printf("\tAlias addresses : %s\r\n", u_addrtoa(&nat->alias_addr,buf,sizeof(buf))); Printf("\tTarget addresses: %s\r\n", u_addrtoa(&nat->target_addr,buf,sizeof(buf))); #ifdef NG_NAT_DESC_LENGTH Printf("Redirect ports:\r\n"); for (k=0;knrpt_id[k] != 0) { struct protoent *proto; char li[16], ai[16], ri[16]; inet_ntop(AF_INET, &nat->nrpt[k].local_addr, li, sizeof(li)); inet_ntop(AF_INET, &nat->nrpt[k].alias_addr, ai, sizeof(ai)); inet_ntop(AF_INET, &nat->nrpt[k].remote_addr, ri, sizeof(ri)); proto = getprotobynumber(nat->nrpt[k].proto); Printf("\t%s %s:%d %s:%d %s:%d (%sactive)\r\n", proto->p_name, ai, nat->nrpt[k].alias_port, li, nat->nrpt[k].local_port, ri, nat->nrpt[k].remote_port, nat->nrpt_id[k]<0?"in":""); } } Printf("Redirect address:\r\n"); for (k=0;knrad_id[k] != 0) { char li[16], ai[16]; inet_ntop(AF_INET, &nat->nrad[k].local_addr, li, sizeof(li)); inet_ntop(AF_INET, &nat->nrad[k].alias_addr, ai, sizeof(ai)); Printf("\t%s %s (%sactive)\r\n", ai, li, nat->nrad_id[k]<0?"in":""); } } Printf("Redirect proto:\r\n"); for (k=0;knrpr_id[k] != 0) { struct protoent *proto; char li[16], ai[16], ri[16]; proto = getprotobynumber(nat->nrpr[k].proto); inet_ntop(AF_INET, &nat->nrpr[k].local_addr, li, sizeof(li)); inet_ntop(AF_INET, &nat->nrpr[k].alias_addr, ai, sizeof(ai)); inet_ntop(AF_INET, &nat->nrpr[k].remote_addr, ri, sizeof(ri)); Printf("\t%s %s %s %s (%sactive)\r\n", proto->p_name, ai, li, ri, nat->nrpr_id[k]<0?"in":""); } } #endif Printf("NAT options:\r\n"); OptStat(ctx, &nat->options, gConfList); #ifdef NG_NAT_LIBALIAS_INFO if (Enabled(&nat->options, NAT_CONF_LOG) && iface->up && iface->nat_up) { snprintf(path, sizeof(path), "mpd%d-%s-nat:", gPid, \ (char *)&ctx->bund->name); bzero(nli, sizeof(struct ng_nat_libalias_info)); Printf("LibAlias statistic:\r\n"); if (NgFuncSendQuery(path, NGM_NAT_COOKIE, NGM_NAT_LIBALIAS_INFO, NULL, 0, &u.reply, sizeof(u), NULL) < 0) Perror("Can't get LibAlias stats"); Printf("\ticmpLinkCount : %u\r\n", nli->icmpLinkCount); Printf("\tudpLinkCount : %u\r\n", nli->udpLinkCount); Printf("\ttcpLinkCount : %u\r\n", nli->tcpLinkCount); Printf("\tsctpLinkCount : %u\r\n", nli->sctpLinkCount); Printf("\tpptpLinkCount : %u\r\n", nli->pptpLinkCount); Printf("\tprotoLinkCount : %u\r\n", nli->protoLinkCount); Printf("\tfragmentIdLinkCount : %u\r\n", nli->fragmentIdLinkCount); Printf("\tfragmentPtrLinkCount : %u\r\n", nli->fragmentPtrLinkCount); Printf("\tsockCount : %u\r\n", nli->sockCount); } #endif return(0); }