1145937Sglebius/*- 2145937Sglebius * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org> 3145937Sglebius * All rights reserved. 4145937Sglebius * 5145937Sglebius * Redistribution and use in source and binary forms, with or without 6145937Sglebius * modification, are permitted provided that the following conditions 7145937Sglebius * are met: 8145937Sglebius * 1. Redistributions of source code must retain the above copyright 9145937Sglebius * notice, this list of conditions and the following disclaimer. 10145937Sglebius * 2. Redistributions in binary form must reproduce the above copyright 11145937Sglebius * notice, this list of conditions and the following disclaimer in the 12145937Sglebius * documentation and/or other materials provided with the distribution. 13145937Sglebius * 14145937Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15145937Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16145937Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17145937Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18145937Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19145937Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20145937Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21145937Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22145937Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23145937Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24145937Sglebius * SUCH DAMAGE. 25145937Sglebius * 26145937Sglebius * $FreeBSD$ 27145937Sglebius */ 28145937Sglebius 29145937Sglebius#include <sys/param.h> 30145937Sglebius#include <sys/systm.h> 31145937Sglebius#include <sys/kernel.h> 32145937Sglebius#include <sys/mbuf.h> 33145937Sglebius#include <sys/malloc.h> 34145937Sglebius#include <sys/ctype.h> 35145937Sglebius#include <sys/errno.h> 36145937Sglebius#include <sys/syslog.h> 37145937Sglebius 38145937Sglebius#include <netinet/in_systm.h> 39145937Sglebius#include <netinet/in.h> 40145937Sglebius#include <netinet/ip.h> 41147625Sglebius#include <netinet/ip_var.h> 42145937Sglebius#include <netinet/tcp.h> 43147625Sglebius#include <machine/in_cksum.h> 44145937Sglebius 45145937Sglebius#include <netinet/libalias/alias.h> 46222808Sae#include <netinet/libalias/alias_local.h> 47145937Sglebius 48145937Sglebius#include <netgraph/ng_message.h> 49145937Sglebius#include <netgraph/ng_parse.h> 50145937Sglebius#include <netgraph/ng_nat.h> 51145937Sglebius#include <netgraph/netgraph.h> 52145937Sglebius 53145937Sglebiusstatic ng_constructor_t ng_nat_constructor; 54145937Sglebiusstatic ng_rcvmsg_t ng_nat_rcvmsg; 55145937Sglebiusstatic ng_shutdown_t ng_nat_shutdown; 56145937Sglebiusstatic ng_newhook_t ng_nat_newhook; 57145937Sglebiusstatic ng_rcvdata_t ng_nat_rcvdata; 58145937Sglebiusstatic ng_disconnect_t ng_nat_disconnect; 59145937Sglebius 60169867Smavstatic unsigned int ng_nat_translate_flags(unsigned int x); 61169867Smav 62169867Smav/* Parse type for struct ng_nat_mode. */ 63169867Smavstatic const struct ng_parse_struct_field ng_nat_mode_fields[] 64169867Smav = NG_NAT_MODE_INFO; 65169867Smavstatic const struct ng_parse_type ng_nat_mode_type = { 66169867Smav &ng_parse_struct_type, 67176706Smav &ng_nat_mode_fields 68169867Smav}; 69169867Smav 70176706Smav/* Parse type for 'description' field in structs. */ 71176706Smavstatic const struct ng_parse_fixedstring_info ng_nat_description_info 72176706Smav = { NG_NAT_DESC_LENGTH }; 73176706Smavstatic const struct ng_parse_type ng_nat_description_type = { 74176706Smav &ng_parse_fixedstring_type, 75176706Smav &ng_nat_description_info 76176706Smav}; 77176706Smav 78176706Smav/* Parse type for struct ng_nat_redirect_port. */ 79176706Smavstatic const struct ng_parse_struct_field ng_nat_redirect_port_fields[] 80176706Smav = NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type); 81176706Smavstatic const struct ng_parse_type ng_nat_redirect_port_type = { 82176706Smav &ng_parse_struct_type, 83176706Smav &ng_nat_redirect_port_fields 84176706Smav}; 85176706Smav 86176706Smav/* Parse type for struct ng_nat_redirect_addr. */ 87176706Smavstatic const struct ng_parse_struct_field ng_nat_redirect_addr_fields[] 88176706Smav = NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type); 89176706Smavstatic const struct ng_parse_type ng_nat_redirect_addr_type = { 90176706Smav &ng_parse_struct_type, 91176706Smav &ng_nat_redirect_addr_fields 92176706Smav}; 93176706Smav 94176706Smav/* Parse type for struct ng_nat_redirect_proto. */ 95176706Smavstatic const struct ng_parse_struct_field ng_nat_redirect_proto_fields[] 96176706Smav = NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type); 97176706Smavstatic const struct ng_parse_type ng_nat_redirect_proto_type = { 98176706Smav &ng_parse_struct_type, 99176706Smav &ng_nat_redirect_proto_fields 100176706Smav}; 101176706Smav 102176706Smav/* Parse type for struct ng_nat_add_server. */ 103176706Smavstatic const struct ng_parse_struct_field ng_nat_add_server_fields[] 104176706Smav = NG_NAT_ADD_SERVER_TYPE_INFO; 105176706Smavstatic const struct ng_parse_type ng_nat_add_server_type = { 106176706Smav &ng_parse_struct_type, 107176706Smav &ng_nat_add_server_fields 108176706Smav}; 109176706Smav 110176706Smav/* Parse type for one struct ng_nat_listrdrs_entry. */ 111176706Smavstatic const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[] 112176706Smav = NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type); 113176706Smavstatic const struct ng_parse_type ng_nat_listrdrs_entry_type = { 114176706Smav &ng_parse_struct_type, 115176706Smav &ng_nat_listrdrs_entry_fields 116176706Smav}; 117176706Smav 118176706Smav/* Parse type for 'redirects' array in struct ng_nat_list_redirects. */ 119176706Smavstatic int 120176706Smavng_nat_listrdrs_ary_getLength(const struct ng_parse_type *type, 121176706Smav const u_char *start, const u_char *buf) 122176706Smav{ 123176706Smav const struct ng_nat_list_redirects *lr; 124176706Smav 125176706Smav lr = (const struct ng_nat_list_redirects *) 126176706Smav (buf - offsetof(struct ng_nat_list_redirects, redirects)); 127176706Smav return lr->total_count; 128176706Smav} 129176706Smav 130176706Smavstatic const struct ng_parse_array_info ng_nat_listrdrs_ary_info = { 131176706Smav &ng_nat_listrdrs_entry_type, 132176706Smav &ng_nat_listrdrs_ary_getLength, 133176706Smav NULL 134176706Smav}; 135176706Smavstatic const struct ng_parse_type ng_nat_listrdrs_ary_type = { 136176706Smav &ng_parse_array_type, 137176706Smav &ng_nat_listrdrs_ary_info 138176706Smav}; 139176706Smav 140176706Smav/* Parse type for struct ng_nat_list_redirects. */ 141176706Smavstatic const struct ng_parse_struct_field ng_nat_list_redirects_fields[] 142176706Smav = NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type); 143176706Smavstatic const struct ng_parse_type ng_nat_list_redirects_type = { 144176706Smav &ng_parse_struct_type, 145176706Smav &ng_nat_list_redirects_fields 146176706Smav}; 147176706Smav 148248570Sglebius/* Parse type for struct ng_nat_libalias_info. */ 149248570Sglebiusstatic const struct ng_parse_struct_field ng_nat_libalias_info_fields[] 150248570Sglebius = NG_NAT_LIBALIAS_INFO; 151248570Sglebiusstatic const struct ng_parse_type ng_nat_libalias_info_type = { 152248570Sglebius &ng_parse_struct_type, 153248570Sglebius &ng_nat_libalias_info_fields 154248570Sglebius}; 155248570Sglebius 156145937Sglebius/* List of commands and how to convert arguments to/from ASCII. */ 157145937Sglebiusstatic const struct ng_cmdlist ng_nat_cmdlist[] = { 158145937Sglebius { 159145937Sglebius NGM_NAT_COOKIE, 160145937Sglebius NGM_NAT_SET_IPADDR, 161145937Sglebius "setaliasaddr", 162145937Sglebius &ng_parse_ipaddr_type, 163145937Sglebius NULL 164145937Sglebius }, 165169867Smav { 166169867Smav NGM_NAT_COOKIE, 167169867Smav NGM_NAT_SET_MODE, 168169867Smav "setmode", 169169867Smav &ng_nat_mode_type, 170169867Smav NULL 171169867Smav }, 172169867Smav { 173169867Smav NGM_NAT_COOKIE, 174169867Smav NGM_NAT_SET_TARGET, 175169867Smav "settarget", 176169867Smav &ng_parse_ipaddr_type, 177169867Smav NULL 178169867Smav }, 179176706Smav { 180176706Smav NGM_NAT_COOKIE, 181176706Smav NGM_NAT_REDIRECT_PORT, 182176706Smav "redirectport", 183176706Smav &ng_nat_redirect_port_type, 184176706Smav &ng_parse_uint32_type 185176706Smav }, 186176706Smav { 187176706Smav NGM_NAT_COOKIE, 188176706Smav NGM_NAT_REDIRECT_ADDR, 189176706Smav "redirectaddr", 190176706Smav &ng_nat_redirect_addr_type, 191176706Smav &ng_parse_uint32_type 192176706Smav }, 193176706Smav { 194176706Smav NGM_NAT_COOKIE, 195176706Smav NGM_NAT_REDIRECT_PROTO, 196176706Smav "redirectproto", 197176706Smav &ng_nat_redirect_proto_type, 198176706Smav &ng_parse_uint32_type 199176706Smav }, 200176706Smav { 201176706Smav NGM_NAT_COOKIE, 202176706Smav NGM_NAT_REDIRECT_DYNAMIC, 203176706Smav "redirectdynamic", 204176706Smav &ng_parse_uint32_type, 205176706Smav NULL 206176706Smav }, 207176706Smav { 208176706Smav NGM_NAT_COOKIE, 209176706Smav NGM_NAT_REDIRECT_DELETE, 210176706Smav "redirectdelete", 211176706Smav &ng_parse_uint32_type, 212176706Smav NULL 213176706Smav }, 214176706Smav { 215176706Smav NGM_NAT_COOKIE, 216176706Smav NGM_NAT_ADD_SERVER, 217176706Smav "addserver", 218176706Smav &ng_nat_add_server_type, 219176706Smav NULL 220176706Smav }, 221176706Smav { 222176706Smav NGM_NAT_COOKIE, 223176706Smav NGM_NAT_LIST_REDIRECTS, 224176706Smav "listredirects", 225176706Smav NULL, 226176706Smav &ng_nat_list_redirects_type 227176706Smav }, 228176706Smav { 229176706Smav NGM_NAT_COOKIE, 230176706Smav NGM_NAT_PROXY_RULE, 231176706Smav "proxyrule", 232176706Smav &ng_parse_string_type, 233176706Smav NULL 234176706Smav }, 235248570Sglebius { 236248570Sglebius NGM_NAT_COOKIE, 237248570Sglebius NGM_NAT_LIBALIAS_INFO, 238248570Sglebius "libaliasinfo", 239248570Sglebius NULL, 240248570Sglebius &ng_nat_libalias_info_type 241248570Sglebius }, 242145937Sglebius { 0 } 243145937Sglebius}; 244145937Sglebius 245145937Sglebius/* Netgraph node type descriptor. */ 246145937Sglebiusstatic struct ng_type typestruct = { 247145937Sglebius .version = NG_ABI_VERSION, 248145937Sglebius .name = NG_NAT_NODE_TYPE, 249145937Sglebius .constructor = ng_nat_constructor, 250145937Sglebius .rcvmsg = ng_nat_rcvmsg, 251145937Sglebius .shutdown = ng_nat_shutdown, 252145937Sglebius .newhook = ng_nat_newhook, 253145937Sglebius .rcvdata = ng_nat_rcvdata, 254145937Sglebius .disconnect = ng_nat_disconnect, 255145937Sglebius .cmdlist = ng_nat_cmdlist, 256145937Sglebius}; 257145937SglebiusNETGRAPH_INIT(nat, &typestruct); 258145937SglebiusMODULE_DEPEND(ng_nat, libalias, 1, 1, 1); 259145937Sglebius 260176706Smav/* Element for list of redirects. */ 261176706Smavstruct ng_nat_rdr_lst { 262176706Smav STAILQ_ENTRY(ng_nat_rdr_lst) entries; 263176706Smav struct alias_link *lnk; 264176706Smav struct ng_nat_listrdrs_entry rdr; 265176706Smav}; 266176706SmavSTAILQ_HEAD(rdrhead, ng_nat_rdr_lst); 267176706Smav 268145937Sglebius/* Information we store for each node. */ 269163297Sglebiusstruct ng_nat_priv { 270145937Sglebius node_p node; /* back pointer to node */ 271145937Sglebius hook_p in; /* hook for demasquerading */ 272145937Sglebius hook_p out; /* hook for masquerading */ 273145937Sglebius struct libalias *lib; /* libalias handler */ 274145937Sglebius uint32_t flags; /* status flags */ 275176706Smav uint32_t rdrcount; /* number or redirects in list */ 276176706Smav uint32_t nextid; /* for next in turn in list */ 277176706Smav struct rdrhead redirhead; /* redirect list header */ 278145937Sglebius}; 279163297Sglebiustypedef struct ng_nat_priv *priv_p; 280145937Sglebius 281145937Sglebius/* Values of flags */ 282169866Smav#define NGNAT_CONNECTED 0x1 /* We have both hooks connected */ 283145937Sglebius#define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */ 284145937Sglebius 285145937Sglebiusstatic int 286145937Sglebiusng_nat_constructor(node_p node) 287145937Sglebius{ 288145937Sglebius priv_p priv; 289145937Sglebius 290145937Sglebius /* Initialize private descriptor. */ 291220768Sglebius priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 292145937Sglebius 293145937Sglebius /* Init aliasing engine. */ 294145937Sglebius priv->lib = LibAliasInit(NULL); 295145937Sglebius 296145937Sglebius /* Set same ports on. */ 297145937Sglebius (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS, 298145937Sglebius PKT_ALIAS_SAME_PORTS); 299145937Sglebius 300176706Smav /* Init redirects housekeeping. */ 301176706Smav priv->rdrcount = 0; 302176706Smav priv->nextid = 1; 303176706Smav STAILQ_INIT(&priv->redirhead); 304176706Smav 305145937Sglebius /* Link structs together. */ 306145937Sglebius NG_NODE_SET_PRIVATE(node, priv); 307145937Sglebius priv->node = node; 308145937Sglebius 309145937Sglebius /* 310145937Sglebius * libalias is not thread safe, so our node 311145937Sglebius * must be single threaded. 312145937Sglebius */ 313145937Sglebius NG_NODE_FORCE_WRITER(node); 314145937Sglebius 315145937Sglebius return (0); 316145937Sglebius} 317145937Sglebius 318145937Sglebiusstatic int 319145937Sglebiusng_nat_newhook(node_p node, hook_p hook, const char *name) 320145937Sglebius{ 321145937Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 322145937Sglebius 323145937Sglebius if (strcmp(name, NG_NAT_HOOK_IN) == 0) { 324145937Sglebius priv->in = hook; 325145937Sglebius } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) { 326145937Sglebius priv->out = hook; 327145937Sglebius } else 328145937Sglebius return (EINVAL); 329145937Sglebius 330145937Sglebius if (priv->out != NULL && 331169866Smav priv->in != NULL) 332169866Smav priv->flags |= NGNAT_CONNECTED; 333145937Sglebius 334145937Sglebius return(0); 335145937Sglebius} 336145937Sglebius 337145937Sglebiusstatic int 338145937Sglebiusng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook) 339145937Sglebius{ 340145937Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 341145937Sglebius struct ng_mesg *resp = NULL; 342145937Sglebius struct ng_mesg *msg; 343145937Sglebius int error = 0; 344145937Sglebius 345145937Sglebius NGI_GET_MSG(item, msg); 346145937Sglebius 347145937Sglebius switch (msg->header.typecookie) { 348145937Sglebius case NGM_NAT_COOKIE: 349145937Sglebius switch (msg->header.cmd) { 350145937Sglebius case NGM_NAT_SET_IPADDR: 351145937Sglebius { 352145937Sglebius struct in_addr *const ia = (struct in_addr *)msg->data; 353145937Sglebius 354145937Sglebius if (msg->header.arglen < sizeof(*ia)) { 355145937Sglebius error = EINVAL; 356145937Sglebius break; 357145937Sglebius } 358145937Sglebius 359145937Sglebius LibAliasSetAddress(priv->lib, *ia); 360145937Sglebius 361145937Sglebius priv->flags |= NGNAT_ADDR_DEFINED; 362145937Sglebius } 363145937Sglebius break; 364169867Smav case NGM_NAT_SET_MODE: 365169867Smav { 366169867Smav struct ng_nat_mode *const mode = 367169867Smav (struct ng_nat_mode *)msg->data; 368169867Smav 369169867Smav if (msg->header.arglen < sizeof(*mode)) { 370169867Smav error = EINVAL; 371169867Smav break; 372169867Smav } 373169867Smav 374169867Smav if (LibAliasSetMode(priv->lib, 375169867Smav ng_nat_translate_flags(mode->flags), 376169867Smav ng_nat_translate_flags(mode->mask)) < 0) { 377169867Smav error = ENOMEM; 378169867Smav break; 379169867Smav } 380169867Smav } 381169867Smav break; 382169867Smav case NGM_NAT_SET_TARGET: 383169867Smav { 384169867Smav struct in_addr *const ia = (struct in_addr *)msg->data; 385169867Smav 386169867Smav if (msg->header.arglen < sizeof(*ia)) { 387169867Smav error = EINVAL; 388169867Smav break; 389169867Smav } 390169867Smav 391169867Smav LibAliasSetTarget(priv->lib, *ia); 392169867Smav } 393169867Smav break; 394176706Smav case NGM_NAT_REDIRECT_PORT: 395176706Smav { 396176706Smav struct ng_nat_rdr_lst *entry; 397176706Smav struct ng_nat_redirect_port *const rp = 398176706Smav (struct ng_nat_redirect_port *)msg->data; 399176706Smav 400176706Smav if (msg->header.arglen < sizeof(*rp)) { 401176706Smav error = EINVAL; 402176706Smav break; 403176706Smav } 404176706Smav 405176706Smav if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 406176706Smav M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 407176706Smav error = ENOMEM; 408176706Smav break; 409176706Smav } 410176706Smav 411176706Smav /* Try actual redirect. */ 412176706Smav entry->lnk = LibAliasRedirectPort(priv->lib, 413176706Smav rp->local_addr, htons(rp->local_port), 414176706Smav rp->remote_addr, htons(rp->remote_port), 415176706Smav rp->alias_addr, htons(rp->alias_port), 416176706Smav rp->proto); 417176706Smav 418176706Smav if (entry->lnk == NULL) { 419176706Smav error = ENOMEM; 420184205Sdes free(entry, M_NETGRAPH); 421176706Smav break; 422176706Smav } 423176706Smav 424176706Smav /* Successful, save info in our internal list. */ 425176706Smav entry->rdr.local_addr = rp->local_addr; 426176706Smav entry->rdr.alias_addr = rp->alias_addr; 427176706Smav entry->rdr.remote_addr = rp->remote_addr; 428176706Smav entry->rdr.local_port = rp->local_port; 429176706Smav entry->rdr.alias_port = rp->alias_port; 430176706Smav entry->rdr.remote_port = rp->remote_port; 431176706Smav entry->rdr.proto = rp->proto; 432176706Smav bcopy(rp->description, entry->rdr.description, 433176706Smav NG_NAT_DESC_LENGTH); 434176706Smav 435176706Smav /* Safety precaution. */ 436176706Smav entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 437176706Smav 438176706Smav entry->rdr.id = priv->nextid++; 439176706Smav priv->rdrcount++; 440176706Smav 441176706Smav /* Link to list of redirects. */ 442176706Smav STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 443176706Smav 444176706Smav /* Response with id of newly added entry. */ 445176706Smav NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 446176706Smav if (resp == NULL) { 447176706Smav error = ENOMEM; 448176706Smav break; 449176706Smav } 450176706Smav bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 451176706Smav } 452176706Smav break; 453176706Smav case NGM_NAT_REDIRECT_ADDR: 454176706Smav { 455176706Smav struct ng_nat_rdr_lst *entry; 456176706Smav struct ng_nat_redirect_addr *const ra = 457176706Smav (struct ng_nat_redirect_addr *)msg->data; 458176706Smav 459176706Smav if (msg->header.arglen < sizeof(*ra)) { 460176706Smav error = EINVAL; 461176706Smav break; 462176706Smav } 463176706Smav 464176706Smav if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 465176706Smav M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 466176706Smav error = ENOMEM; 467176706Smav break; 468176706Smav } 469176706Smav 470176706Smav /* Try actual redirect. */ 471176706Smav entry->lnk = LibAliasRedirectAddr(priv->lib, 472176706Smav ra->local_addr, ra->alias_addr); 473176706Smav 474176706Smav if (entry->lnk == NULL) { 475176706Smav error = ENOMEM; 476184205Sdes free(entry, M_NETGRAPH); 477176706Smav break; 478176706Smav } 479176706Smav 480176706Smav /* Successful, save info in our internal list. */ 481176706Smav entry->rdr.local_addr = ra->local_addr; 482176706Smav entry->rdr.alias_addr = ra->alias_addr; 483176706Smav entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR; 484176706Smav bcopy(ra->description, entry->rdr.description, 485176706Smav NG_NAT_DESC_LENGTH); 486176706Smav 487176706Smav /* Safety precaution. */ 488176706Smav entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 489176706Smav 490176706Smav entry->rdr.id = priv->nextid++; 491176706Smav priv->rdrcount++; 492176706Smav 493176706Smav /* Link to list of redirects. */ 494176706Smav STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 495176706Smav 496176706Smav /* Response with id of newly added entry. */ 497176706Smav NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 498176706Smav if (resp == NULL) { 499176706Smav error = ENOMEM; 500176706Smav break; 501176706Smav } 502176706Smav bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 503176706Smav } 504176706Smav break; 505176706Smav case NGM_NAT_REDIRECT_PROTO: 506176706Smav { 507176706Smav struct ng_nat_rdr_lst *entry; 508176706Smav struct ng_nat_redirect_proto *const rp = 509176706Smav (struct ng_nat_redirect_proto *)msg->data; 510176706Smav 511176706Smav if (msg->header.arglen < sizeof(*rp)) { 512176706Smav error = EINVAL; 513176706Smav break; 514176706Smav } 515176706Smav 516176706Smav if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 517176706Smav M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 518176706Smav error = ENOMEM; 519176706Smav break; 520176706Smav } 521176706Smav 522176706Smav /* Try actual redirect. */ 523176706Smav entry->lnk = LibAliasRedirectProto(priv->lib, 524176706Smav rp->local_addr, rp->remote_addr, 525176706Smav rp->alias_addr, rp->proto); 526176706Smav 527176706Smav if (entry->lnk == NULL) { 528176706Smav error = ENOMEM; 529184205Sdes free(entry, M_NETGRAPH); 530176706Smav break; 531176706Smav } 532176706Smav 533176706Smav /* Successful, save info in our internal list. */ 534176706Smav entry->rdr.local_addr = rp->local_addr; 535176706Smav entry->rdr.alias_addr = rp->alias_addr; 536176706Smav entry->rdr.remote_addr = rp->remote_addr; 537176706Smav entry->rdr.proto = rp->proto; 538176706Smav bcopy(rp->description, entry->rdr.description, 539176706Smav NG_NAT_DESC_LENGTH); 540176706Smav 541176706Smav /* Safety precaution. */ 542176706Smav entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 543176706Smav 544176706Smav entry->rdr.id = priv->nextid++; 545176706Smav priv->rdrcount++; 546176706Smav 547176706Smav /* Link to list of redirects. */ 548176706Smav STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 549176706Smav 550176706Smav /* Response with id of newly added entry. */ 551176706Smav NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 552176706Smav if (resp == NULL) { 553176706Smav error = ENOMEM; 554176706Smav break; 555176706Smav } 556176706Smav bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 557176706Smav } 558176706Smav break; 559176706Smav case NGM_NAT_REDIRECT_DYNAMIC: 560176706Smav case NGM_NAT_REDIRECT_DELETE: 561176706Smav { 562176706Smav struct ng_nat_rdr_lst *entry; 563176706Smav uint32_t *const id = (uint32_t *)msg->data; 564176706Smav 565176706Smav if (msg->header.arglen < sizeof(*id)) { 566176706Smav error = EINVAL; 567176706Smav break; 568176706Smav } 569176706Smav 570176706Smav /* Find entry with supplied id. */ 571176706Smav STAILQ_FOREACH(entry, &priv->redirhead, entries) { 572176706Smav if (entry->rdr.id == *id) 573176706Smav break; 574176706Smav } 575176706Smav 576176706Smav /* Not found. */ 577176706Smav if (entry == NULL) { 578176706Smav error = ENOENT; 579176706Smav break; 580176706Smav } 581176706Smav 582176706Smav if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) { 583176706Smav if (LibAliasRedirectDynamic(priv->lib, 584176706Smav entry->lnk) == -1) { 585176706Smav error = ENOTTY; /* XXX Something better? */ 586176706Smav break; 587176706Smav } 588176706Smav } else { /* NGM_NAT_REDIRECT_DELETE */ 589176706Smav LibAliasRedirectDelete(priv->lib, entry->lnk); 590176706Smav } 591176706Smav 592176706Smav /* Delete entry from our internal list. */ 593176706Smav priv->rdrcount--; 594176706Smav STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries); 595184205Sdes free(entry, M_NETGRAPH); 596176706Smav } 597176706Smav break; 598176706Smav case NGM_NAT_ADD_SERVER: 599176706Smav { 600176706Smav struct ng_nat_rdr_lst *entry; 601176706Smav struct ng_nat_add_server *const as = 602176706Smav (struct ng_nat_add_server *)msg->data; 603176706Smav 604176706Smav if (msg->header.arglen < sizeof(*as)) { 605176706Smav error = EINVAL; 606176706Smav break; 607176706Smav } 608176706Smav 609176706Smav /* Find entry with supplied id. */ 610176706Smav STAILQ_FOREACH(entry, &priv->redirhead, entries) { 611176706Smav if (entry->rdr.id == as->id) 612176706Smav break; 613176706Smav } 614176706Smav 615176706Smav /* Not found. */ 616176706Smav if (entry == NULL) { 617176706Smav error = ENOENT; 618176706Smav break; 619176706Smav } 620176706Smav 621176706Smav if (LibAliasAddServer(priv->lib, entry->lnk, 622176706Smav as->addr, htons(as->port)) == -1) { 623176706Smav error = ENOMEM; 624176706Smav break; 625176706Smav } 626176706Smav 627176706Smav entry->rdr.lsnat++; 628176706Smav } 629176706Smav break; 630176706Smav case NGM_NAT_LIST_REDIRECTS: 631176706Smav { 632176706Smav struct ng_nat_rdr_lst *entry; 633176706Smav struct ng_nat_list_redirects *ary; 634176706Smav int i = 0; 635176706Smav 636176706Smav NG_MKRESPONSE(resp, msg, sizeof(*ary) + 637176706Smav (priv->rdrcount) * sizeof(*entry), M_NOWAIT); 638176706Smav if (resp == NULL) { 639176706Smav error = ENOMEM; 640176706Smav break; 641176706Smav } 642176706Smav 643176706Smav ary = (struct ng_nat_list_redirects *)resp->data; 644176706Smav ary->total_count = priv->rdrcount; 645176706Smav 646176706Smav STAILQ_FOREACH(entry, &priv->redirhead, entries) { 647176706Smav bcopy(&entry->rdr, &ary->redirects[i++], 648176706Smav sizeof(struct ng_nat_listrdrs_entry)); 649176706Smav } 650176706Smav } 651176706Smav break; 652176706Smav case NGM_NAT_PROXY_RULE: 653176706Smav { 654176706Smav char *cmd = (char *)msg->data; 655176706Smav 656176706Smav if (msg->header.arglen < 6) { 657176706Smav error = EINVAL; 658176706Smav break; 659176706Smav } 660176706Smav 661176706Smav if (LibAliasProxyRule(priv->lib, cmd) != 0) 662176706Smav error = ENOMEM; 663176706Smav } 664176706Smav break; 665248570Sglebius case NGM_NAT_LIBALIAS_INFO: 666248570Sglebius { 667248570Sglebius struct ng_nat_libalias_info *i; 668248570Sglebius 669248570Sglebius NG_MKRESPONSE(resp, msg, 670248570Sglebius sizeof(struct ng_nat_libalias_info), M_NOWAIT); 671248570Sglebius if (resp == NULL) { 672248570Sglebius error = ENOMEM; 673248570Sglebius break; 674248570Sglebius } 675248570Sglebius i = (struct ng_nat_libalias_info *)resp->data; 676248570Sglebius#define COPY(F) do { \ 677248570Sglebius if (priv->lib->F >= 0 && priv->lib->F < UINT32_MAX) \ 678248570Sglebius i->F = priv->lib->F; \ 679248570Sglebius else \ 680248570Sglebius i->F = UINT32_MAX; \ 681248570Sglebius} while (0) 682248570Sglebius 683248570Sglebius COPY(icmpLinkCount); 684248570Sglebius COPY(udpLinkCount); 685248570Sglebius COPY(tcpLinkCount); 686248570Sglebius COPY(pptpLinkCount); 687248570Sglebius COPY(sctpLinkCount); 688248570Sglebius COPY(protoLinkCount); 689248570Sglebius COPY(fragmentIdLinkCount); 690248570Sglebius COPY(fragmentPtrLinkCount); 691248570Sglebius COPY(sockCount); 692248570Sglebius#undef COPY 693248570Sglebius } 694248570Sglebius break; 695145937Sglebius default: 696145937Sglebius error = EINVAL; /* unknown command */ 697145937Sglebius break; 698145937Sglebius } 699145937Sglebius break; 700145937Sglebius default: 701145937Sglebius error = EINVAL; /* unknown cookie type */ 702145937Sglebius break; 703145937Sglebius } 704145937Sglebius 705145937Sglebius NG_RESPOND_MSG(error, node, item, resp); 706145937Sglebius NG_FREE_MSG(msg); 707145937Sglebius return (error); 708145937Sglebius} 709145937Sglebius 710145937Sglebiusstatic int 711145937Sglebiusng_nat_rcvdata(hook_p hook, item_p item ) 712145937Sglebius{ 713145937Sglebius const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 714145937Sglebius struct mbuf *m; 715146063Sglebius struct ip *ip; 716145937Sglebius int rval, error = 0; 717145937Sglebius char *c; 718145937Sglebius 719169866Smav /* We have no required hooks. */ 720169866Smav if (!(priv->flags & NGNAT_CONNECTED)) { 721145937Sglebius NG_FREE_ITEM(item); 722145937Sglebius return (ENXIO); 723145937Sglebius } 724145937Sglebius 725169866Smav /* We have no alias address yet to do anything. */ 726169866Smav if (!(priv->flags & NGNAT_ADDR_DEFINED)) 727169866Smav goto send; 728169866Smav 729145937Sglebius m = NGI_M(item); 730145937Sglebius 731145937Sglebius if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) { 732145937Sglebius NGI_M(item) = NULL; /* avoid double free */ 733145937Sglebius NG_FREE_ITEM(item); 734145937Sglebius return (ENOBUFS); 735145937Sglebius } 736145937Sglebius 737145937Sglebius NGI_M(item) = m; 738145937Sglebius 739146084Sglebius c = mtod(m, char *); 740146084Sglebius ip = mtod(m, struct ip *); 741146084Sglebius 742146063Sglebius KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len), 743146063Sglebius ("ng_nat: ip_len != m_pkthdr.len")); 744146063Sglebius 745222808Sae /* 746222808Sae * We drop packet when: 747222808Sae * 1. libalias returns PKT_ALIAS_ERROR; 748222808Sae * 2. For incoming packets: 749222808Sae * a) for unresolved fragments; 750222808Sae * b) libalias returns PKT_ALIAS_IGNORED and 751222808Sae * PKT_ALIAS_DENY_INCOMING flag is set. 752222808Sae */ 753145937Sglebius if (hook == priv->in) { 754179477Smav rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m)); 755222808Sae if (rval == PKT_ALIAS_ERROR || 756222808Sae rval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 757222808Sae (rval == PKT_ALIAS_IGNORED && 758222808Sae (priv->lib->packetAliasMode & 759222808Sae PKT_ALIAS_DENY_INCOMING) != 0)) { 760145937Sglebius NG_FREE_ITEM(item); 761145937Sglebius return (EINVAL); 762145937Sglebius } 763145937Sglebius } else if (hook == priv->out) { 764179477Smav rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m)); 765222808Sae if (rval == PKT_ALIAS_ERROR) { 766145937Sglebius NG_FREE_ITEM(item); 767145937Sglebius return (EINVAL); 768145937Sglebius } 769145937Sglebius } else 770145937Sglebius panic("ng_nat: unknown hook!\n"); 771145937Sglebius 772222808Sae if (rval == PKT_ALIAS_RESPOND) 773222808Sae m->m_flags |= M_SKIP_FIREWALL; 774147625Sglebius m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len); 775147625Sglebius 776147625Sglebius if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 777147625Sglebius ip->ip_p == IPPROTO_TCP) { 778165119Sglebius struct tcphdr *th = (struct tcphdr *)((caddr_t)ip + 779165119Sglebius (ip->ip_hl << 2)); 780147625Sglebius 781147625Sglebius /* 782147625Sglebius * Here is our terrible HACK. 783147625Sglebius * 784147625Sglebius * Sometimes LibAlias edits contents of TCP packet. 785147625Sglebius * In this case it needs to recompute full TCP 786147625Sglebius * checksum. However, the problem is that LibAlias 787147625Sglebius * doesn't have any idea about checksum offloading 788147625Sglebius * in kernel. To workaround this, we do not do 789147625Sglebius * checksumming in LibAlias, but only mark the 790147625Sglebius * packets in th_x2 field. If we receive a marked 791147625Sglebius * packet, we calculate correct checksum for it 792147625Sglebius * aware of offloading. 793147625Sglebius * 794147625Sglebius * Why do I do such a terrible hack instead of 795147625Sglebius * recalculating checksum for each packet? 796147625Sglebius * Because the previous checksum was not checked! 797147625Sglebius * Recalculating checksums for EVERY packet will 798147625Sglebius * hide ALL transmission errors. Yes, marked packets 799147625Sglebius * still suffer from this problem. But, sigh, natd(8) 800147625Sglebius * has this problem, too. 801147625Sglebius */ 802147625Sglebius 803147625Sglebius if (th->th_x2) { 804241344Sglebius uint16_t ip_len = ntohs(ip->ip_len); 805241344Sglebius 806147625Sglebius th->th_x2 = 0; 807147625Sglebius th->th_sum = in_pseudo(ip->ip_src.s_addr, 808147625Sglebius ip->ip_dst.s_addr, htons(IPPROTO_TCP + 809241344Sglebius ip_len - (ip->ip_hl << 2))); 810147625Sglebius 811147625Sglebius if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) { 812147625Sglebius m->m_pkthdr.csum_data = offsetof(struct tcphdr, 813147625Sglebius th_sum); 814147625Sglebius in_delayed_cksum(m); 815147625Sglebius } 816147625Sglebius } 817147625Sglebius } 818147625Sglebius 819169866Smavsend: 820147625Sglebius if (hook == priv->in) 821147625Sglebius NG_FWD_ITEM_HOOK(error, item, priv->out); 822147625Sglebius else 823147625Sglebius NG_FWD_ITEM_HOOK(error, item, priv->in); 824147625Sglebius 825145937Sglebius return (error); 826145937Sglebius} 827145937Sglebius 828145937Sglebiusstatic int 829145937Sglebiusng_nat_shutdown(node_p node) 830145937Sglebius{ 831145937Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 832145937Sglebius 833145937Sglebius NG_NODE_SET_PRIVATE(node, NULL); 834145937Sglebius NG_NODE_UNREF(node); 835176706Smav 836176706Smav /* Free redirects list. */ 837176706Smav while (!STAILQ_EMPTY(&priv->redirhead)) { 838176706Smav struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead); 839176706Smav STAILQ_REMOVE_HEAD(&priv->redirhead, entries); 840184205Sdes free(entry, M_NETGRAPH); 841176706Smav }; 842176706Smav 843176706Smav /* Final free. */ 844145937Sglebius LibAliasUninit(priv->lib); 845184205Sdes free(priv, M_NETGRAPH); 846145937Sglebius 847145937Sglebius return (0); 848145937Sglebius} 849145937Sglebius 850145937Sglebiusstatic int 851145937Sglebiusng_nat_disconnect(hook_p hook) 852145937Sglebius{ 853145937Sglebius const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 854145937Sglebius 855169866Smav priv->flags &= ~NGNAT_CONNECTED; 856145937Sglebius 857145937Sglebius if (hook == priv->out) 858145937Sglebius priv->out = NULL; 859145937Sglebius if (hook == priv->in) 860145937Sglebius priv->in = NULL; 861145937Sglebius 862145937Sglebius if (priv->out == NULL && priv->in == NULL) 863145937Sglebius ng_rmnode_self(NG_HOOK_NODE(hook)); 864145937Sglebius 865145937Sglebius return (0); 866145937Sglebius} 867145937Sglebius 868169867Smavstatic unsigned int 869169867Smavng_nat_translate_flags(unsigned int x) 870169867Smav{ 871169867Smav unsigned int res = 0; 872169867Smav 873169867Smav if (x & NG_NAT_LOG) 874169867Smav res |= PKT_ALIAS_LOG; 875169867Smav if (x & NG_NAT_DENY_INCOMING) 876169867Smav res |= PKT_ALIAS_DENY_INCOMING; 877169867Smav if (x & NG_NAT_SAME_PORTS) 878169867Smav res |= PKT_ALIAS_SAME_PORTS; 879169867Smav if (x & NG_NAT_UNREGISTERED_ONLY) 880169867Smav res |= PKT_ALIAS_UNREGISTERED_ONLY; 881169867Smav if (x & NG_NAT_RESET_ON_ADDR_CHANGE) 882169867Smav res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; 883169867Smav if (x & NG_NAT_PROXY_ONLY) 884169867Smav res |= PKT_ALIAS_PROXY_ONLY; 885169867Smav if (x & NG_NAT_REVERSE) 886169867Smav res |= PKT_ALIAS_REVERSE; 887169867Smav 888169867Smav return (res); 889169867Smav} 890