ng_nat.c revision 369375
1189251Ssam/*- 2189251Ssam * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org> 3252726Srpaulo * All rights reserved. 4189251Ssam * 5252726Srpaulo * Redistribution and use in source and binary forms, with or without 6252726Srpaulo * modification, are permitted provided that the following conditions 7189251Ssam * are met: 8189251Ssam * 1. Redistributions of source code must retain the above copyright 9189251Ssam * notice, this list of conditions and the following disclaimer. 10189251Ssam * 2. Redistributions in binary form must reproduce the above copyright 11189251Ssam * notice, this list of conditions and the following disclaimer in the 12189251Ssam * documentation and/or other materials provided with the distribution. 13189251Ssam * 14189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17189251Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19252726Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20252726Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21252726Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22214734Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23252726Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24252726Srpaulo * SUCH DAMAGE. 25252726Srpaulo * 26252726Srpaulo * $FreeBSD: stable/11/sys/netgraph/ng_nat.c 369375 2021-02-25 10:10:36Z donner $ 27189251Ssam */ 28189251Ssam 29252726Srpaulo#include <sys/param.h> 30252726Srpaulo#include <sys/systm.h> 31189251Ssam#include <sys/kernel.h> 32189251Ssam#include <sys/mbuf.h> 33252726Srpaulo#include <sys/malloc.h> 34252726Srpaulo#include <sys/ctype.h> 35252726Srpaulo#include <sys/errno.h> 36252726Srpaulo#include <sys/syslog.h> 37252726Srpaulo 38252726Srpaulo#include <netinet/in_systm.h> 39252726Srpaulo#include <netinet/in.h> 40252726Srpaulo#include <netinet/ip.h> 41252726Srpaulo#include <netinet/ip_var.h> 42252726Srpaulo#include <netinet/tcp.h> 43252726Srpaulo#include <machine/in_cksum.h> 44252726Srpaulo 45252726Srpaulo#include <net/dlt.h> 46252726Srpaulo#include <net/ethernet.h> 47252726Srpaulo 48252726Srpaulo#include <netinet/libalias/alias.h> 49252726Srpaulo#include <netinet/libalias/alias_local.h> 50252726Srpaulo 51252726Srpaulo#include <netgraph/ng_message.h> 52252726Srpaulo#include <netgraph/ng_parse.h> 53252726Srpaulo#include <netgraph/ng_nat.h> 54252726Srpaulo#include <netgraph/netgraph.h> 55252726Srpaulo 56252726Srpaulostatic ng_constructor_t ng_nat_constructor; 57252726Srpaulostatic ng_rcvmsg_t ng_nat_rcvmsg; 58252726Srpaulostatic ng_shutdown_t ng_nat_shutdown; 59252726Srpaulostatic ng_newhook_t ng_nat_newhook; 60252726Srpaulostatic ng_rcvdata_t ng_nat_rcvdata; 61252726Srpaulostatic ng_disconnect_t ng_nat_disconnect; 62252726Srpaulo 63252726Srpaulostatic unsigned int ng_nat_translate_flags(unsigned int x); 64252726Srpaulo 65252726Srpaulo/* Parse type for struct ng_nat_mode. */ 66252726Srpaulostatic const struct ng_parse_struct_field ng_nat_mode_fields[] 67252726Srpaulo = NG_NAT_MODE_INFO; 68252726Srpaulostatic const struct ng_parse_type ng_nat_mode_type = { 69252726Srpaulo &ng_parse_struct_type, 70252726Srpaulo &ng_nat_mode_fields 71252726Srpaulo}; 72252726Srpaulo 73252726Srpaulo/* Parse type for 'description' field in structs. */ 74252726Srpaulostatic const struct ng_parse_fixedstring_info ng_nat_description_info 75252726Srpaulo = { NG_NAT_DESC_LENGTH }; 76252726Srpaulostatic const struct ng_parse_type ng_nat_description_type = { 77252726Srpaulo &ng_parse_fixedstring_type, 78252726Srpaulo &ng_nat_description_info 79252726Srpaulo}; 80252726Srpaulo 81252726Srpaulo/* Parse type for struct ng_nat_redirect_port. */ 82252726Srpaulostatic const struct ng_parse_struct_field ng_nat_redirect_port_fields[] 83252726Srpaulo = NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type); 84252726Srpaulostatic const struct ng_parse_type ng_nat_redirect_port_type = { 85252726Srpaulo &ng_parse_struct_type, 86252726Srpaulo &ng_nat_redirect_port_fields 87252726Srpaulo}; 88252726Srpaulo 89252726Srpaulo/* Parse type for struct ng_nat_redirect_addr. */ 90252726Srpaulostatic const struct ng_parse_struct_field ng_nat_redirect_addr_fields[] 91252726Srpaulo = NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type); 92252726Srpaulostatic const struct ng_parse_type ng_nat_redirect_addr_type = { 93252726Srpaulo &ng_parse_struct_type, 94252726Srpaulo &ng_nat_redirect_addr_fields 95252726Srpaulo}; 96252726Srpaulo 97252726Srpaulo/* Parse type for struct ng_nat_redirect_proto. */ 98252726Srpaulostatic const struct ng_parse_struct_field ng_nat_redirect_proto_fields[] 99252726Srpaulo = NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type); 100252726Srpaulostatic const struct ng_parse_type ng_nat_redirect_proto_type = { 101252726Srpaulo &ng_parse_struct_type, 102252726Srpaulo &ng_nat_redirect_proto_fields 103252726Srpaulo}; 104252726Srpaulo 105252726Srpaulo/* Parse type for struct ng_nat_add_server. */ 106252726Srpaulostatic const struct ng_parse_struct_field ng_nat_add_server_fields[] 107252726Srpaulo = NG_NAT_ADD_SERVER_TYPE_INFO; 108252726Srpaulostatic const struct ng_parse_type ng_nat_add_server_type = { 109252726Srpaulo &ng_parse_struct_type, 110252726Srpaulo &ng_nat_add_server_fields 111252726Srpaulo}; 112252726Srpaulo 113252726Srpaulo/* Parse type for one struct ng_nat_listrdrs_entry. */ 114252726Srpaulostatic const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[] 115252726Srpaulo = NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type); 116252726Srpaulostatic const struct ng_parse_type ng_nat_listrdrs_entry_type = { 117252726Srpaulo &ng_parse_struct_type, 118252726Srpaulo &ng_nat_listrdrs_entry_fields 119252726Srpaulo}; 120252726Srpaulo 121252726Srpaulo/* Parse type for 'redirects' array in struct ng_nat_list_redirects. */ 122252726Srpaulostatic int 123252726Srpaulong_nat_listrdrs_ary_getLength(const struct ng_parse_type *type, 124252726Srpaulo const u_char *start, const u_char *buf) 125252726Srpaulo{ 126252726Srpaulo const struct ng_nat_list_redirects *lr; 127252726Srpaulo 128252726Srpaulo lr = (const struct ng_nat_list_redirects *) 129252726Srpaulo (buf - offsetof(struct ng_nat_list_redirects, redirects)); 130252726Srpaulo return lr->total_count; 131252726Srpaulo} 132252726Srpaulo 133252726Srpaulostatic const struct ng_parse_array_info ng_nat_listrdrs_ary_info = { 134252726Srpaulo &ng_nat_listrdrs_entry_type, 135252726Srpaulo &ng_nat_listrdrs_ary_getLength, 136252726Srpaulo NULL 137252726Srpaulo}; 138252726Srpaulostatic const struct ng_parse_type ng_nat_listrdrs_ary_type = { 139252726Srpaulo &ng_parse_array_type, 140252726Srpaulo &ng_nat_listrdrs_ary_info 141252726Srpaulo}; 142252726Srpaulo 143252726Srpaulo/* Parse type for struct ng_nat_list_redirects. */ 144252726Srpaulostatic const struct ng_parse_struct_field ng_nat_list_redirects_fields[] 145252726Srpaulo = NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type); 146252726Srpaulostatic const struct ng_parse_type ng_nat_list_redirects_type = { 147252726Srpaulo &ng_parse_struct_type, 148252726Srpaulo &ng_nat_list_redirects_fields 149252726Srpaulo}; 150252726Srpaulo 151252726Srpaulo/* Parse type for struct ng_nat_libalias_info. */ 152252726Srpaulostatic const struct ng_parse_struct_field ng_nat_libalias_info_fields[] 153252726Srpaulo = NG_NAT_LIBALIAS_INFO; 154252726Srpaulostatic const struct ng_parse_type ng_nat_libalias_info_type = { 155252726Srpaulo &ng_parse_struct_type, 156252726Srpaulo &ng_nat_libalias_info_fields 157252726Srpaulo}; 158252726Srpaulo 159252726Srpaulo/* List of commands and how to convert arguments to/from ASCII. */ 160252726Srpaulostatic const struct ng_cmdlist ng_nat_cmdlist[] = { 161252726Srpaulo { 162252726Srpaulo NGM_NAT_COOKIE, 163252726Srpaulo NGM_NAT_SET_IPADDR, 164252726Srpaulo "setaliasaddr", 165252726Srpaulo &ng_parse_ipaddr_type, 166252726Srpaulo NULL 167252726Srpaulo }, 168252726Srpaulo { 169252726Srpaulo NGM_NAT_COOKIE, 170252726Srpaulo NGM_NAT_SET_MODE, 171252726Srpaulo "setmode", 172252726Srpaulo &ng_nat_mode_type, 173252726Srpaulo NULL 174252726Srpaulo }, 175252726Srpaulo { 176252726Srpaulo NGM_NAT_COOKIE, 177252726Srpaulo NGM_NAT_SET_TARGET, 178252726Srpaulo "settarget", 179252726Srpaulo &ng_parse_ipaddr_type, 180252726Srpaulo NULL 181252726Srpaulo }, 182252726Srpaulo { 183252726Srpaulo NGM_NAT_COOKIE, 184252726Srpaulo NGM_NAT_REDIRECT_PORT, 185252726Srpaulo "redirectport", 186252726Srpaulo &ng_nat_redirect_port_type, 187252726Srpaulo &ng_parse_uint32_type 188252726Srpaulo }, 189252726Srpaulo { 190252726Srpaulo NGM_NAT_COOKIE, 191252726Srpaulo NGM_NAT_REDIRECT_ADDR, 192252726Srpaulo "redirectaddr", 193252726Srpaulo &ng_nat_redirect_addr_type, 194252726Srpaulo &ng_parse_uint32_type 195252726Srpaulo }, 196252726Srpaulo { 197252726Srpaulo NGM_NAT_COOKIE, 198252726Srpaulo NGM_NAT_REDIRECT_PROTO, 199252726Srpaulo "redirectproto", 200252726Srpaulo &ng_nat_redirect_proto_type, 201252726Srpaulo &ng_parse_uint32_type 202252726Srpaulo }, 203252726Srpaulo { 204252726Srpaulo NGM_NAT_COOKIE, 205252726Srpaulo NGM_NAT_REDIRECT_DYNAMIC, 206252726Srpaulo "redirectdynamic", 207252726Srpaulo &ng_parse_uint32_type, 208252726Srpaulo NULL 209252726Srpaulo }, 210252726Srpaulo { 211252726Srpaulo NGM_NAT_COOKIE, 212252726Srpaulo NGM_NAT_REDIRECT_DELETE, 213252726Srpaulo "redirectdelete", 214252726Srpaulo &ng_parse_uint32_type, 215252726Srpaulo NULL 216252726Srpaulo }, 217252726Srpaulo { 218252726Srpaulo NGM_NAT_COOKIE, 219252726Srpaulo NGM_NAT_ADD_SERVER, 220252726Srpaulo "addserver", 221252726Srpaulo &ng_nat_add_server_type, 222252726Srpaulo NULL 223252726Srpaulo }, 224189251Ssam { 225189251Ssam NGM_NAT_COOKIE, 226189251Ssam NGM_NAT_LIST_REDIRECTS, 227189251Ssam "listredirects", 228189251Ssam NULL, 229189251Ssam &ng_nat_list_redirects_type 230189251Ssam }, 231189251Ssam { 232189251Ssam NGM_NAT_COOKIE, 233189251Ssam NGM_NAT_PROXY_RULE, 234189251Ssam "proxyrule", 235189251Ssam &ng_parse_string_type, 236189251Ssam NULL 237189251Ssam }, 238189251Ssam { 239189251Ssam NGM_NAT_COOKIE, 240189251Ssam NGM_NAT_LIBALIAS_INFO, 241189251Ssam "libaliasinfo", 242189251Ssam NULL, 243189251Ssam &ng_nat_libalias_info_type 244189251Ssam }, 245189251Ssam { 246189251Ssam NGM_NAT_COOKIE, 247189251Ssam NGM_NAT_SET_DLT, 248189251Ssam "setdlt", 249189251Ssam &ng_parse_uint8_type, 250189251Ssam NULL 251189251Ssam }, 252189251Ssam { 253189251Ssam NGM_NAT_COOKIE, 254252726Srpaulo NGM_NAT_GET_DLT, 255252726Srpaulo "getdlt", 256252726Srpaulo NULL, 257252726Srpaulo &ng_parse_uint8_type 258252726Srpaulo }, 259252726Srpaulo { 0 } 260252726Srpaulo}; 261189251Ssam 262189251Ssam/* Netgraph node type descriptor. */ 263189251Ssamstatic struct ng_type typestruct = { 264189251Ssam .version = NG_ABI_VERSION, 265189251Ssam .name = NG_NAT_NODE_TYPE, 266189251Ssam .constructor = ng_nat_constructor, 267189251Ssam .rcvmsg = ng_nat_rcvmsg, 268189251Ssam .shutdown = ng_nat_shutdown, 269189251Ssam .newhook = ng_nat_newhook, 270189251Ssam .rcvdata = ng_nat_rcvdata, 271189251Ssam .disconnect = ng_nat_disconnect, 272189251Ssam .cmdlist = ng_nat_cmdlist, 273189251Ssam}; 274189251SsamNETGRAPH_INIT(nat, &typestruct); 275189251SsamMODULE_DEPEND(ng_nat, libalias, 1, 1, 1); 276189251Ssam 277189251Ssam/* Element for list of redirects. */ 278189251Ssamstruct ng_nat_rdr_lst { 279189251Ssam STAILQ_ENTRY(ng_nat_rdr_lst) entries; 280189251Ssam struct alias_link *lnk; 281189251Ssam struct ng_nat_listrdrs_entry rdr; 282189251Ssam}; 283189251SsamSTAILQ_HEAD(rdrhead, ng_nat_rdr_lst); 284189251Ssam 285189251Ssam/* Information we store for each node. */ 286189251Ssamstruct ng_nat_priv { 287189251Ssam node_p node; /* back pointer to node */ 288189251Ssam hook_p in; /* hook for demasquerading */ 289189251Ssam hook_p out; /* hook for masquerading */ 290189251Ssam struct libalias *lib; /* libalias handler */ 291189251Ssam uint32_t flags; /* status flags */ 292189251Ssam uint32_t rdrcount; /* number or redirects in list */ 293189251Ssam uint32_t nextid; /* for next in turn in list */ 294189251Ssam struct rdrhead redirhead; /* redirect list header */ 295189251Ssam uint8_t dlt; /* DLT_XXX from bpf.h */ 296189251Ssam}; 297189251Ssamtypedef struct ng_nat_priv *priv_p; 298189251Ssam 299189251Ssam/* Values of flags */ 300189251Ssam#define NGNAT_CONNECTED 0x1 /* We have both hooks connected */ 301189251Ssam#define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */ 302252726Srpaulo 303252726Srpaulostatic int 304252726Srpaulong_nat_constructor(node_p node) 305252726Srpaulo{ 306252726Srpaulo priv_p priv; 307252726Srpaulo 308252726Srpaulo /* Initialize private descriptor. */ 309252726Srpaulo priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 310252726Srpaulo 311189251Ssam /* Init aliasing engine. */ 312189251Ssam priv->lib = LibAliasInit(NULL); 313189251Ssam 314189251Ssam /* Set same ports on. */ 315189251Ssam (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS, 316252726Srpaulo PKT_ALIAS_SAME_PORTS); 317189251Ssam 318189251Ssam /* Init redirects housekeeping. */ 319189251Ssam priv->rdrcount = 0; 320189251Ssam priv->nextid = 1; 321189251Ssam priv->dlt = DLT_RAW; 322189251Ssam STAILQ_INIT(&priv->redirhead); 323189251Ssam 324189251Ssam /* Link structs together. */ 325189251Ssam NG_NODE_SET_PRIVATE(node, priv); 326189251Ssam priv->node = node; 327189251Ssam 328189251Ssam /* 329189251Ssam * libalias is not thread safe, so our node 330189251Ssam * must be single threaded. 331189251Ssam */ 332189251Ssam NG_NODE_FORCE_WRITER(node); 333189251Ssam 334189251Ssam return (0); 335189251Ssam} 336189251Ssam 337189251Ssamstatic int 338189251Ssamng_nat_newhook(node_p node, hook_p hook, const char *name) 339189251Ssam{ 340189251Ssam const priv_p priv = NG_NODE_PRIVATE(node); 341189251Ssam 342189251Ssam if (strcmp(name, NG_NAT_HOOK_IN) == 0) { 343189251Ssam priv->in = hook; 344189251Ssam } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) { 345189251Ssam priv->out = hook; 346189251Ssam } else 347189251Ssam return (EINVAL); 348189251Ssam 349189251Ssam if (priv->out != NULL && 350189251Ssam priv->in != NULL) 351189251Ssam priv->flags |= NGNAT_CONNECTED; 352189251Ssam 353189251Ssam return(0); 354189251Ssam} 355189251Ssam 356189251Ssamstatic int 357189251Ssamng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook) 358189251Ssam{ 359189251Ssam const priv_p priv = NG_NODE_PRIVATE(node); 360189251Ssam struct ng_mesg *resp = NULL; 361189251Ssam struct ng_mesg *msg; 362189251Ssam int error = 0; 363189251Ssam 364189251Ssam NGI_GET_MSG(item, msg); 365189251Ssam 366189251Ssam switch (msg->header.typecookie) { 367189251Ssam case NGM_NAT_COOKIE: 368189251Ssam switch (msg->header.cmd) { 369189251Ssam case NGM_NAT_SET_IPADDR: 370189251Ssam { 371189251Ssam struct in_addr *const ia = (struct in_addr *)msg->data; 372189251Ssam 373189251Ssam if (msg->header.arglen < sizeof(*ia)) { 374189251Ssam error = EINVAL; 375189251Ssam break; 376189251Ssam } 377189251Ssam 378189251Ssam LibAliasSetAddress(priv->lib, *ia); 379189251Ssam 380189251Ssam priv->flags |= NGNAT_ADDR_DEFINED; 381189251Ssam } 382189251Ssam break; 383189251Ssam case NGM_NAT_SET_MODE: 384189251Ssam { 385189251Ssam struct ng_nat_mode *const mode = 386189251Ssam (struct ng_nat_mode *)msg->data; 387189251Ssam 388189251Ssam if (msg->header.arglen < sizeof(*mode)) { 389189251Ssam error = EINVAL; 390189251Ssam break; 391189251Ssam } 392189251Ssam 393189251Ssam if (LibAliasSetMode(priv->lib, 394189251Ssam ng_nat_translate_flags(mode->flags), 395189251Ssam ng_nat_translate_flags(mode->mask)) < 0) { 396189251Ssam error = ENOMEM; 397189251Ssam break; 398189251Ssam } 399189251Ssam } 400189251Ssam break; 401189251Ssam case NGM_NAT_SET_TARGET: 402189251Ssam { 403189251Ssam struct in_addr *const ia = (struct in_addr *)msg->data; 404189251Ssam 405189251Ssam if (msg->header.arglen < sizeof(*ia)) { 406189251Ssam error = EINVAL; 407189251Ssam break; 408189251Ssam } 409252726Srpaulo 410252726Srpaulo LibAliasSetTarget(priv->lib, *ia); 411252726Srpaulo } 412252726Srpaulo break; 413252726Srpaulo case NGM_NAT_REDIRECT_PORT: 414252726Srpaulo { 415252726Srpaulo struct ng_nat_rdr_lst *entry; 416252726Srpaulo struct ng_nat_redirect_port *const rp = 417252726Srpaulo (struct ng_nat_redirect_port *)msg->data; 418252726Srpaulo 419252726Srpaulo if (msg->header.arglen < sizeof(*rp)) { 420252726Srpaulo error = EINVAL; 421252726Srpaulo break; 422252726Srpaulo } 423252726Srpaulo 424252726Srpaulo if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 425252726Srpaulo M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 426189251Ssam error = ENOMEM; 427189251Ssam break; 428189251Ssam } 429189251Ssam 430189251Ssam /* Try actual redirect. */ 431189251Ssam entry->lnk = LibAliasRedirectPort(priv->lib, 432189251Ssam rp->local_addr, htons(rp->local_port), 433189251Ssam rp->remote_addr, htons(rp->remote_port), 434189251Ssam rp->alias_addr, htons(rp->alias_port), 435189251Ssam rp->proto); 436189251Ssam 437189251Ssam if (entry->lnk == NULL) { 438189251Ssam error = ENOMEM; 439189251Ssam free(entry, M_NETGRAPH); 440189251Ssam break; 441189251Ssam } 442189251Ssam 443189251Ssam /* Successful, save info in our internal list. */ 444189251Ssam entry->rdr.local_addr = rp->local_addr; 445189251Ssam entry->rdr.alias_addr = rp->alias_addr; 446189251Ssam entry->rdr.remote_addr = rp->remote_addr; 447189251Ssam entry->rdr.local_port = rp->local_port; 448189251Ssam entry->rdr.alias_port = rp->alias_port; 449189251Ssam entry->rdr.remote_port = rp->remote_port; 450189251Ssam entry->rdr.proto = rp->proto; 451189251Ssam bcopy(rp->description, entry->rdr.description, 452189251Ssam NG_NAT_DESC_LENGTH); 453189251Ssam 454189251Ssam /* Safety precaution. */ 455189251Ssam entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 456189251Ssam 457189251Ssam entry->rdr.id = priv->nextid++; 458189251Ssam priv->rdrcount++; 459189251Ssam 460189251Ssam /* Link to list of redirects. */ 461189251Ssam STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 462189251Ssam 463189251Ssam /* Response with id of newly added entry. */ 464189251Ssam NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 465189251Ssam if (resp == NULL) { 466189251Ssam error = ENOMEM; 467189251Ssam break; 468189251Ssam } 469189251Ssam bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 470189251Ssam } 471189251Ssam break; 472189251Ssam case NGM_NAT_REDIRECT_ADDR: 473189251Ssam { 474189251Ssam struct ng_nat_rdr_lst *entry; 475189251Ssam struct ng_nat_redirect_addr *const ra = 476189251Ssam (struct ng_nat_redirect_addr *)msg->data; 477189251Ssam 478189251Ssam if (msg->header.arglen < sizeof(*ra)) { 479189251Ssam error = EINVAL; 480189251Ssam break; 481189251Ssam } 482189251Ssam 483189251Ssam if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 484189251Ssam M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 485189251Ssam error = ENOMEM; 486189251Ssam break; 487189251Ssam } 488189251Ssam 489189251Ssam /* Try actual redirect. */ 490189251Ssam entry->lnk = LibAliasRedirectAddr(priv->lib, 491189251Ssam ra->local_addr, ra->alias_addr); 492189251Ssam 493189251Ssam if (entry->lnk == NULL) { 494189251Ssam error = ENOMEM; 495189251Ssam free(entry, M_NETGRAPH); 496189251Ssam break; 497189251Ssam } 498189251Ssam 499189251Ssam /* Successful, save info in our internal list. */ 500189251Ssam entry->rdr.local_addr = ra->local_addr; 501189251Ssam entry->rdr.alias_addr = ra->alias_addr; 502189251Ssam entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR; 503189251Ssam bcopy(ra->description, entry->rdr.description, 504189251Ssam NG_NAT_DESC_LENGTH); 505189251Ssam 506189251Ssam /* Safety precaution. */ 507189251Ssam entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 508189251Ssam 509189251Ssam entry->rdr.id = priv->nextid++; 510189251Ssam priv->rdrcount++; 511189251Ssam 512189251Ssam /* Link to list of redirects. */ 513189251Ssam STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 514189251Ssam 515189251Ssam /* Response with id of newly added entry. */ 516252726Srpaulo NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 517189251Ssam if (resp == NULL) { 518189251Ssam error = ENOMEM; 519214734Srpaulo break; 520214734Srpaulo } 521214734Srpaulo bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 522252726Srpaulo } 523252726Srpaulo break; 524214734Srpaulo case NGM_NAT_REDIRECT_PROTO: 525252726Srpaulo { 526252726Srpaulo struct ng_nat_rdr_lst *entry; 527252726Srpaulo struct ng_nat_redirect_proto *const rp = 528214734Srpaulo (struct ng_nat_redirect_proto *)msg->data; 529214734Srpaulo 530214734Srpaulo if (msg->header.arglen < sizeof(*rp)) { 531214734Srpaulo error = EINVAL; 532189251Ssam break; 533189251Ssam } 534189251Ssam 535189251Ssam if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 536189251Ssam M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 537189251Ssam error = ENOMEM; 538189251Ssam break; 539189251Ssam } 540189251Ssam 541189251Ssam /* Try actual redirect. */ 542189251Ssam entry->lnk = LibAliasRedirectProto(priv->lib, 543189251Ssam rp->local_addr, rp->remote_addr, 544189251Ssam rp->alias_addr, rp->proto); 545189251Ssam 546189251Ssam if (entry->lnk == NULL) { 547189251Ssam error = ENOMEM; 548189251Ssam free(entry, M_NETGRAPH); 549189251Ssam break; 550189251Ssam } 551189251Ssam 552189251Ssam /* Successful, save info in our internal list. */ 553189251Ssam entry->rdr.local_addr = rp->local_addr; 554189251Ssam entry->rdr.alias_addr = rp->alias_addr; 555214734Srpaulo entry->rdr.remote_addr = rp->remote_addr; 556252726Srpaulo entry->rdr.proto = rp->proto; 557214734Srpaulo bcopy(rp->description, entry->rdr.description, 558252726Srpaulo NG_NAT_DESC_LENGTH); 559252726Srpaulo 560252726Srpaulo /* Safety precaution. */ 561252726Srpaulo entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 562252726Srpaulo 563252726Srpaulo entry->rdr.id = priv->nextid++; 564252726Srpaulo priv->rdrcount++; 565252726Srpaulo 566252726Srpaulo /* Link to list of redirects. */ 567252726Srpaulo STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 568252726Srpaulo 569252726Srpaulo /* Response with id of newly added entry. */ 570252726Srpaulo NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 571252726Srpaulo if (resp == NULL) { 572252726Srpaulo error = ENOMEM; 573252726Srpaulo break; 574252726Srpaulo } 575252726Srpaulo bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 576252726Srpaulo } 577252726Srpaulo break; 578252726Srpaulo case NGM_NAT_REDIRECT_DYNAMIC: 579252726Srpaulo case NGM_NAT_REDIRECT_DELETE: 580252726Srpaulo { 581252726Srpaulo struct ng_nat_rdr_lst *entry; 582252726Srpaulo uint32_t *const id = (uint32_t *)msg->data; 583252726Srpaulo 584252726Srpaulo if (msg->header.arglen < sizeof(*id)) { 585252726Srpaulo error = EINVAL; 586252726Srpaulo break; 587252726Srpaulo } 588252726Srpaulo 589252726Srpaulo /* Find entry with supplied id. */ 590252726Srpaulo STAILQ_FOREACH(entry, &priv->redirhead, entries) { 591252726Srpaulo if (entry->rdr.id == *id) 592252726Srpaulo break; 593252726Srpaulo } 594252726Srpaulo 595252726Srpaulo /* Not found. */ 596252726Srpaulo if (entry == NULL) { 597252726Srpaulo error = ENOENT; 598252726Srpaulo break; 599252726Srpaulo } 600252726Srpaulo 601214734Srpaulo if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) { 602214734Srpaulo if (LibAliasRedirectDynamic(priv->lib, 603214734Srpaulo entry->lnk) == -1) { 604214734Srpaulo error = ENOTTY; /* XXX Something better? */ 605214734Srpaulo break; 606252726Srpaulo } 607252726Srpaulo } else { /* NGM_NAT_REDIRECT_DELETE */ 608252726Srpaulo LibAliasRedirectDelete(priv->lib, entry->lnk); 609252726Srpaulo } 610252726Srpaulo 611252726Srpaulo /* Delete entry from our internal list. */ 612252726Srpaulo priv->rdrcount--; 613252726Srpaulo STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries); 614252726Srpaulo free(entry, M_NETGRAPH); 615252726Srpaulo } 616252726Srpaulo break; 617252726Srpaulo case NGM_NAT_ADD_SERVER: 618252726Srpaulo { 619252726Srpaulo struct ng_nat_rdr_lst *entry; 620252726Srpaulo struct ng_nat_add_server *const as = 621252726Srpaulo (struct ng_nat_add_server *)msg->data; 622252726Srpaulo 623252726Srpaulo if (msg->header.arglen < sizeof(*as)) { 624252726Srpaulo error = EINVAL; 625214734Srpaulo break; 626214734Srpaulo } 627214734Srpaulo 628214734Srpaulo /* Find entry with supplied id. */ 629214734Srpaulo STAILQ_FOREACH(entry, &priv->redirhead, entries) { 630214734Srpaulo if (entry->rdr.id == as->id) 631252726Srpaulo break; 632252726Srpaulo } 633252726Srpaulo 634252726Srpaulo /* Not found. */ 635252726Srpaulo if (entry == NULL) { 636252726Srpaulo error = ENOENT; 637252726Srpaulo break; 638252726Srpaulo } 639252726Srpaulo 640252726Srpaulo if (LibAliasAddServer(priv->lib, entry->lnk, 641252726Srpaulo as->addr, htons(as->port)) == -1) { 642252726Srpaulo error = ENOMEM; 643252726Srpaulo break; 644252726Srpaulo } 645252726Srpaulo 646252726Srpaulo entry->rdr.lsnat++; 647252726Srpaulo } 648252726Srpaulo break; 649252726Srpaulo case NGM_NAT_LIST_REDIRECTS: 650252726Srpaulo { 651252726Srpaulo struct ng_nat_rdr_lst *entry; 652252726Srpaulo struct ng_nat_list_redirects *ary; 653252726Srpaulo int i = 0; 654252726Srpaulo 655252726Srpaulo NG_MKRESPONSE(resp, msg, sizeof(*ary) + 656252726Srpaulo (priv->rdrcount) * sizeof(*entry), M_NOWAIT); 657252726Srpaulo if (resp == NULL) { 658252726Srpaulo error = ENOMEM; 659252726Srpaulo break; 660252726Srpaulo } 661252726Srpaulo 662252726Srpaulo ary = (struct ng_nat_list_redirects *)resp->data; 663252726Srpaulo ary->total_count = priv->rdrcount; 664252726Srpaulo 665252726Srpaulo STAILQ_FOREACH(entry, &priv->redirhead, entries) { 666252726Srpaulo bcopy(&entry->rdr, &ary->redirects[i++], 667252726Srpaulo sizeof(struct ng_nat_listrdrs_entry)); 668252726Srpaulo } 669252726Srpaulo } 670252726Srpaulo break; 671252726Srpaulo case NGM_NAT_PROXY_RULE: 672252726Srpaulo { 673252726Srpaulo char *cmd = (char *)msg->data; 674252726Srpaulo 675252726Srpaulo if (msg->header.arglen < 6) { 676252726Srpaulo error = EINVAL; 677252726Srpaulo break; 678252726Srpaulo } 679252726Srpaulo 680252726Srpaulo if (LibAliasProxyRule(priv->lib, cmd) != 0) 681252726Srpaulo error = ENOMEM; 682252726Srpaulo } 683252726Srpaulo break; 684252726Srpaulo case NGM_NAT_LIBALIAS_INFO: 685252726Srpaulo { 686252726Srpaulo struct ng_nat_libalias_info *i; 687252726Srpaulo 688252726Srpaulo NG_MKRESPONSE(resp, msg, 689252726Srpaulo sizeof(struct ng_nat_libalias_info), M_NOWAIT); 690252726Srpaulo if (resp == NULL) { 691252726Srpaulo error = ENOMEM; 692252726Srpaulo break; 693252726Srpaulo } 694252726Srpaulo i = (struct ng_nat_libalias_info *)resp->data; 695252726Srpaulo#define COPY(F) do { \ 696252726Srpaulo if (priv->lib->F >= 0 && priv->lib->F < UINT32_MAX) \ 697252726Srpaulo i->F = priv->lib->F; \ 698252726Srpaulo else \ 699252726Srpaulo i->F = UINT32_MAX; \ 700252726Srpaulo} while (0) 701252726Srpaulo 702252726Srpaulo COPY(icmpLinkCount); 703252726Srpaulo COPY(udpLinkCount); 704252726Srpaulo COPY(tcpLinkCount); 705252726Srpaulo COPY(pptpLinkCount); 706252726Srpaulo COPY(sctpLinkCount); 707252726Srpaulo COPY(protoLinkCount); 708252726Srpaulo COPY(fragmentIdLinkCount); 709252726Srpaulo COPY(fragmentPtrLinkCount); 710252726Srpaulo COPY(sockCount); 711252726Srpaulo#undef COPY 712252726Srpaulo } 713252726Srpaulo break; 714252726Srpaulo case NGM_NAT_SET_DLT: 715252726Srpaulo if (msg->header.arglen != sizeof(uint8_t)) { 716252726Srpaulo error = EINVAL; 717252726Srpaulo break; 718252726Srpaulo } 719252726Srpaulo switch (*(uint8_t *) msg->data) { 720252726Srpaulo case DLT_EN10MB: 721252726Srpaulo case DLT_RAW: 722252726Srpaulo priv->dlt = *(uint8_t *) msg->data; 723252726Srpaulo break; 724252726Srpaulo default: 725252726Srpaulo error = EINVAL; 726252726Srpaulo break; 727252726Srpaulo } 728252726Srpaulo break; 729252726Srpaulo default: 730252726Srpaulo error = EINVAL; /* unknown command */ 731252726Srpaulo break; 732252726Srpaulo } 733252726Srpaulo break; 734252726Srpaulo case NGM_NAT_GET_DLT: 735252726Srpaulo NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK); 736252726Srpaulo if (resp == NULL) { 737252726Srpaulo error = ENOMEM; 738252726Srpaulo break; 739252726Srpaulo } 740252726Srpaulo *((uint8_t *) resp->data) = priv->dlt; 741252726Srpaulo break; 742252726Srpaulo default: 743252726Srpaulo error = EINVAL; /* unknown cookie type */ 744252726Srpaulo break; 745252726Srpaulo } 746252726Srpaulo 747252726Srpaulo NG_RESPOND_MSG(error, node, item, resp); 748252726Srpaulo NG_FREE_MSG(msg); 749252726Srpaulo return (error); 750252726Srpaulo} 751252726Srpaulo 752252726Srpaulostatic int 753252726Srpaulong_nat_rcvdata(hook_p hook, item_p item ) 754252726Srpaulo{ 755252726Srpaulo const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 756252726Srpaulo struct mbuf *m; 757252726Srpaulo struct ip *ip; 758252726Srpaulo int rval, ipofs, error = 0; 759252726Srpaulo char *c; 760252726Srpaulo 761252726Srpaulo /* We have no required hooks. */ 762252726Srpaulo if (!(priv->flags & NGNAT_CONNECTED)) { 763252726Srpaulo NG_FREE_ITEM(item); 764252726Srpaulo return (ENXIO); 765252726Srpaulo } 766252726Srpaulo 767252726Srpaulo /* We have no alias address yet to do anything. */ 768252726Srpaulo if (!(priv->flags & NGNAT_ADDR_DEFINED)) 769252726Srpaulo goto send; 770252726Srpaulo 771252726Srpaulo m = NGI_M(item); 772252726Srpaulo 773252726Srpaulo if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) { 774252726Srpaulo NGI_M(item) = NULL; /* avoid double free */ 775252726Srpaulo NG_FREE_ITEM(item); 776252726Srpaulo return (ENOBUFS); 777252726Srpaulo } 778252726Srpaulo 779252726Srpaulo NGI_M(item) = m; 780252726Srpaulo 781252726Srpaulo switch (priv->dlt) { 782252726Srpaulo case DLT_RAW: 783252726Srpaulo ipofs = 0; 784252726Srpaulo break; 785252726Srpaulo case DLT_EN10MB: 786252726Srpaulo { 787252726Srpaulo struct ether_header *eh; 788252726Srpaulo 789252726Srpaulo if (m->m_pkthdr.len < sizeof(struct ether_header)) { 790252726Srpaulo NG_FREE_ITEM(item); 791252726Srpaulo return (ENXIO); 792252726Srpaulo } 793252726Srpaulo eh = mtod(m, struct ether_header *); 794252726Srpaulo switch (ntohs(eh->ether_type)) { 795252726Srpaulo case ETHERTYPE_IP: 796252726Srpaulo ipofs = sizeof(struct ether_header); 797252726Srpaulo break; 798252726Srpaulo default: 799252726Srpaulo goto send; 800189251Ssam } 801189251Ssam break; 802189251Ssam } 803189251Ssam default: 804189251Ssam panic("Corrupted priv->dlt: %u", priv->dlt); 805189251Ssam } 806189251Ssam 807252726Srpaulo if (m->m_pkthdr.len < ipofs + sizeof(struct ip)) 808252726Srpaulo goto send; /* packet too short to hold IP */ 809252726Srpaulo 810189251Ssam c = (char *)mtodo(m, ipofs); 811189251Ssam ip = (struct ip *)mtodo(m, ipofs); 812189251Ssam 813189251Ssam if (ip->ip_v != IPVERSION) 814189251Ssam goto send; /* other IP version, let it pass */ 815189251Ssam if (m->m_pkthdr.len < ipofs + ntohs(ip->ip_len)) 816252726Srpaulo goto send; /* packet too short (i.e. fragmented or broken) */ 817252726Srpaulo 818214734Srpaulo /* 819189251Ssam * We drop packet when: 820189251Ssam * 1. libalias returns PKT_ALIAS_ERROR; 821189251Ssam * 2. For incoming packets: 822189251Ssam * a) for unresolved fragments; 823189251Ssam * b) libalias returns PKT_ALIAS_IGNORED and 824214734Srpaulo * PKT_ALIAS_DENY_INCOMING flag is set. 825189251Ssam */ 826189251Ssam if (hook == priv->in) { 827189251Ssam rval = LibAliasIn(priv->lib, c, m->m_len - ipofs + 828189251Ssam M_TRAILINGSPACE(m)); 829189251Ssam if (rval == PKT_ALIAS_ERROR || 830189251Ssam rval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 831189251Ssam (rval == PKT_ALIAS_IGNORED && 832252726Srpaulo (priv->lib->packetAliasMode & 833252726Srpaulo PKT_ALIAS_DENY_INCOMING) != 0)) { 834252726Srpaulo NG_FREE_ITEM(item); 835252726Srpaulo return (EINVAL); 836252726Srpaulo } 837252726Srpaulo } else if (hook == priv->out) { 838252726Srpaulo rval = LibAliasOut(priv->lib, c, m->m_len - ipofs + 839189251Ssam M_TRAILINGSPACE(m)); 840189251Ssam if (rval == PKT_ALIAS_ERROR) { 841189251Ssam NG_FREE_ITEM(item); 842189251Ssam return (EINVAL); 843189251Ssam } 844189251Ssam } else 845189251Ssam panic("ng_nat: unknown hook!\n"); 846189251Ssam 847189251Ssam if (rval == PKT_ALIAS_RESPOND) 848252726Srpaulo m->m_flags |= M_SKIP_FIREWALL; 849252726Srpaulo m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len) + ipofs; 850252726Srpaulo 851252726Srpaulo if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 852189251Ssam ip->ip_p == IPPROTO_TCP) { 853189251Ssam struct tcphdr *th = (struct tcphdr *)((caddr_t)ip + 854189251Ssam (ip->ip_hl << 2)); 855189251Ssam 856189251Ssam /* 857189251Ssam * Here is our terrible HACK. 858189251Ssam * 859189251Ssam * Sometimes LibAlias edits contents of TCP packet. 860189251Ssam * In this case it needs to recompute full TCP 861189251Ssam * checksum. However, the problem is that LibAlias 862189251Ssam * doesn't have any idea about checksum offloading 863189251Ssam * in kernel. To workaround this, we do not do 864189251Ssam * checksumming in LibAlias, but only mark the 865189251Ssam * packets in th_x2 field. If we receive a marked 866189251Ssam * packet, we calculate correct checksum for it 867189251Ssam * aware of offloading. 868189251Ssam * 869189251Ssam * Why do I do such a terrible hack instead of 870189251Ssam * recalculating checksum for each packet? 871189251Ssam * Because the previous checksum was not checked! 872189251Ssam * Recalculating checksums for EVERY packet will 873189251Ssam * hide ALL transmission errors. Yes, marked packets 874189251Ssam * still suffer from this problem. But, sigh, natd(8) 875189251Ssam * has this problem, too. 876189251Ssam */ 877189251Ssam 878189251Ssam if (th->th_x2) { 879189251Ssam uint16_t ip_len = ntohs(ip->ip_len); 880189251Ssam 881189251Ssam th->th_x2 = 0; 882189251Ssam th->th_sum = in_pseudo(ip->ip_src.s_addr, 883189251Ssam ip->ip_dst.s_addr, htons(IPPROTO_TCP + 884189251Ssam ip_len - (ip->ip_hl << 2))); 885 886 if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) { 887 m->m_pkthdr.csum_data = offsetof(struct tcphdr, 888 th_sum); 889 in_delayed_cksum(m); 890 } 891 } 892 } 893 894send: 895 if (hook == priv->in) 896 NG_FWD_ITEM_HOOK(error, item, priv->out); 897 else 898 NG_FWD_ITEM_HOOK(error, item, priv->in); 899 900 return (error); 901} 902 903static int 904ng_nat_shutdown(node_p node) 905{ 906 const priv_p priv = NG_NODE_PRIVATE(node); 907 908 NG_NODE_SET_PRIVATE(node, NULL); 909 NG_NODE_UNREF(node); 910 911 /* Free redirects list. */ 912 while (!STAILQ_EMPTY(&priv->redirhead)) { 913 struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead); 914 STAILQ_REMOVE_HEAD(&priv->redirhead, entries); 915 free(entry, M_NETGRAPH); 916 } 917 918 /* Final free. */ 919 LibAliasUninit(priv->lib); 920 free(priv, M_NETGRAPH); 921 922 return (0); 923} 924 925static int 926ng_nat_disconnect(hook_p hook) 927{ 928 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 929 930 priv->flags &= ~NGNAT_CONNECTED; 931 932 if (hook == priv->out) 933 priv->out = NULL; 934 if (hook == priv->in) 935 priv->in = NULL; 936 937 if (priv->out == NULL && priv->in == NULL) 938 ng_rmnode_self(NG_HOOK_NODE(hook)); 939 940 return (0); 941} 942 943static unsigned int 944ng_nat_translate_flags(unsigned int x) 945{ 946 unsigned int res = 0; 947 948 if (x & NG_NAT_LOG) 949 res |= PKT_ALIAS_LOG; 950 if (x & NG_NAT_DENY_INCOMING) 951 res |= PKT_ALIAS_DENY_INCOMING; 952 if (x & NG_NAT_SAME_PORTS) 953 res |= PKT_ALIAS_SAME_PORTS; 954 if (x & NG_NAT_UNREGISTERED_ONLY) 955 res |= PKT_ALIAS_UNREGISTERED_ONLY; 956 if (x & NG_NAT_RESET_ON_ADDR_CHANGE) 957 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; 958 if (x & NG_NAT_PROXY_ONLY) 959 res |= PKT_ALIAS_PROXY_ONLY; 960 if (x & NG_NAT_REVERSE) 961 res |= PKT_ALIAS_REVERSE; 962 if (x & NG_NAT_UNREGISTERED_CGN) 963 res |= PKT_ALIAS_UNREGISTERED_CGN; 964 965 return (res); 966} 967