1200590Sluigi/*- 2200855Sluigi * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa 3200590Sluigi * 4200855Sluigi * Supported by: Valeria Paoli 5200855Sluigi * 6200590Sluigi * Redistribution and use in source and binary forms, with or without 7200590Sluigi * modification, are permitted provided that the following conditions 8200590Sluigi * are met: 9200590Sluigi * 1. Redistributions of source code must retain the above copyright 10200590Sluigi * notice, this list of conditions and the following disclaimer. 11200590Sluigi * 2. Redistributions in binary form must reproduce the above copyright 12200590Sluigi * notice, this list of conditions and the following disclaimer in the 13200590Sluigi * documentation and/or other materials provided with the distribution. 14200590Sluigi * 15200590Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16200590Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17200590Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18200590Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19200590Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20200590Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21200590Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22200590Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23200590Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24200590Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25200590Sluigi * SUCH DAMAGE. 26200590Sluigi */ 27200590Sluigi 28200590Sluigi#include <sys/cdefs.h> 29200590Sluigi__FBSDID("$FreeBSD$"); 30200590Sluigi 31200590Sluigi/* 32200601Sluigi * Sockopt support for ipfw. The routines here implement 33200601Sluigi * the upper half of the ipfw code. 34200590Sluigi */ 35200590Sluigi 36225518Sjhb#include "opt_ipfw.h" 37200590Sluigi#include "opt_inet.h" 38200590Sluigi#ifndef INET 39200590Sluigi#error IPFIREWALL requires INET. 40200590Sluigi#endif /* INET */ 41200590Sluigi#include "opt_inet6.h" 42200590Sluigi 43200590Sluigi#include <sys/param.h> 44200590Sluigi#include <sys/systm.h> 45200590Sluigi#include <sys/malloc.h> 46200603Sluigi#include <sys/mbuf.h> /* struct m_tag used by nested headers */ 47200590Sluigi#include <sys/kernel.h> 48200590Sluigi#include <sys/lock.h> 49200590Sluigi#include <sys/priv.h> 50200590Sluigi#include <sys/proc.h> 51200590Sluigi#include <sys/rwlock.h> 52200590Sluigi#include <sys/socket.h> 53200590Sluigi#include <sys/socketvar.h> 54200590Sluigi#include <sys/sysctl.h> 55200590Sluigi#include <sys/syslog.h> 56200590Sluigi#include <net/if.h> 57200590Sluigi#include <net/route.h> 58200590Sluigi#include <net/vnet.h> 59200590Sluigi 60200590Sluigi#include <netinet/in.h> 61201732Sluigi#include <netinet/ip_var.h> /* hooks */ 62200590Sluigi#include <netinet/ip_fw.h> 63200590Sluigi 64240494Sglebius#include <netpfil/ipfw/ip_fw_private.h> 65240494Sglebius 66200590Sluigi#ifdef MAC 67200590Sluigi#include <security/mac/mac_framework.h> 68200590Sluigi#endif 69200590Sluigi 70200601SluigiMALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); 71200590Sluigi 72200601Sluigi/* 73200838Sluigi * static variables followed by global ones (none in this file) 74200601Sluigi */ 75200601Sluigi 76200590Sluigi/* 77200855Sluigi * Find the smallest rule >= key, id. 78200855Sluigi * We could use bsearch but it is so simple that we code it directly 79200590Sluigi */ 80200855Sluigiint 81200855Sluigiipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id) 82200590Sluigi{ 83200855Sluigi int i, lo, hi; 84200855Sluigi struct ip_fw *r; 85200590Sluigi 86200855Sluigi for (lo = 0, hi = chain->n_rules - 1; lo < hi;) { 87200855Sluigi i = (lo + hi) / 2; 88200855Sluigi r = chain->map[i]; 89200855Sluigi if (r->rulenum < key) 90200855Sluigi lo = i + 1; /* continue from the next one */ 91200855Sluigi else if (r->rulenum > key) 92200855Sluigi hi = i; /* this might be good */ 93200855Sluigi else if (r->id < id) 94200855Sluigi lo = i + 1; /* continue from the next one */ 95200855Sluigi else /* r->id >= id */ 96200855Sluigi hi = i; /* this might be good */ 97200855Sluigi }; 98200855Sluigi return hi; 99200855Sluigi} 100200590Sluigi 101200855Sluigi/* 102200855Sluigi * allocate a new map, returns the chain locked. extra is the number 103200855Sluigi * of entries to add or delete. 104200855Sluigi */ 105200855Sluigistatic struct ip_fw ** 106200855Sluigiget_map(struct ip_fw_chain *chain, int extra, int locked) 107200855Sluigi{ 108200590Sluigi 109200855Sluigi for (;;) { 110200855Sluigi struct ip_fw **map; 111200855Sluigi int i; 112200855Sluigi 113200855Sluigi i = chain->n_rules + extra; 114204591Sluigi map = malloc(i * sizeof(struct ip_fw *), M_IPFW, 115204591Sluigi locked ? M_NOWAIT : M_WAITOK); 116200855Sluigi if (map == NULL) { 117200855Sluigi printf("%s: cannot allocate map\n", __FUNCTION__); 118200855Sluigi return NULL; 119200855Sluigi } 120200855Sluigi if (!locked) 121200855Sluigi IPFW_UH_WLOCK(chain); 122200855Sluigi if (i >= chain->n_rules + extra) /* good */ 123200855Sluigi return map; 124200855Sluigi /* otherwise we lost the race, free and retry */ 125200855Sluigi if (!locked) 126200855Sluigi IPFW_UH_WUNLOCK(chain); 127200855Sluigi free(map, M_IPFW); 128200855Sluigi } 129200590Sluigi} 130200590Sluigi 131200590Sluigi/* 132200855Sluigi * swap the maps. It is supposed to be called with IPFW_UH_WLOCK 133200855Sluigi */ 134200855Sluigistatic struct ip_fw ** 135200855Sluigiswap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len) 136200855Sluigi{ 137200855Sluigi struct ip_fw **old_map; 138200855Sluigi 139200855Sluigi IPFW_WLOCK(chain); 140200855Sluigi chain->id++; 141200855Sluigi chain->n_rules = new_len; 142200855Sluigi old_map = chain->map; 143200855Sluigi chain->map = new_map; 144200855Sluigi IPFW_WUNLOCK(chain); 145200855Sluigi return old_map; 146200855Sluigi} 147200855Sluigi 148200855Sluigi/* 149200590Sluigi * Add a new rule to the list. Copy the rule into a malloc'ed area, then 150200590Sluigi * possibly create a rule number and add the rule to the list. 151200590Sluigi * Update the rule_number in the input struct so the caller knows it as well. 152200855Sluigi * XXX DO NOT USE FOR THE DEFAULT RULE. 153200855Sluigi * Must be called without IPFW_UH held 154200590Sluigi */ 155200590Sluigiint 156200590Sluigiipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule) 157200590Sluigi{ 158200855Sluigi struct ip_fw *rule; 159200855Sluigi int i, l, insert_before; 160200855Sluigi struct ip_fw **map; /* the new array of pointers */ 161200590Sluigi 162200855Sluigi if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1) 163200590Sluigi return (EINVAL); 164200590Sluigi 165200855Sluigi l = RULESIZE(input_rule); 166200855Sluigi rule = malloc(l, M_IPFW, M_WAITOK | M_ZERO); 167200855Sluigi /* get_map returns with IPFW_UH_WLOCK if successful */ 168200855Sluigi map = get_map(chain, 1, 0 /* not locked */); 169200855Sluigi if (map == NULL) { 170200855Sluigi free(rule, M_IPFW); 171200855Sluigi return ENOSPC; 172200855Sluigi } 173200590Sluigi 174200590Sluigi bcopy(input_rule, rule, l); 175200855Sluigi /* clear fields not settable from userland */ 176200855Sluigi rule->x_next = NULL; 177200590Sluigi rule->next_rule = NULL; 178243711Smelifaro IPFW_ZERO_RULE_COUNTER(rule); 179200590Sluigi 180200590Sluigi if (V_autoinc_step < 1) 181200590Sluigi V_autoinc_step = 1; 182200590Sluigi else if (V_autoinc_step > 1000) 183200590Sluigi V_autoinc_step = 1000; 184200855Sluigi /* find the insertion point, we will insert before */ 185200855Sluigi insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE; 186200855Sluigi i = ipfw_find_rule(chain, insert_before, 0); 187200855Sluigi /* duplicate first part */ 188200855Sluigi if (i > 0) 189200855Sluigi bcopy(chain->map, map, i * sizeof(struct ip_fw *)); 190200855Sluigi map[i] = rule; 191200855Sluigi /* duplicate remaining part, we always have the default rule */ 192200855Sluigi bcopy(chain->map + i, map + i + 1, 193200855Sluigi sizeof(struct ip_fw *) *(chain->n_rules - i)); 194200590Sluigi if (rule->rulenum == 0) { 195200855Sluigi /* write back the number */ 196200855Sluigi rule->rulenum = i > 0 ? map[i-1]->rulenum : 0; 197200590Sluigi if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) 198200590Sluigi rule->rulenum += V_autoinc_step; 199200590Sluigi input_rule->rulenum = rule->rulenum; 200200590Sluigi } 201200590Sluigi 202200855Sluigi rule->id = chain->id + 1; 203200855Sluigi map = swap_map(chain, map, chain->n_rules + 1); 204200838Sluigi chain->static_len += l; 205200855Sluigi IPFW_UH_WUNLOCK(chain); 206200855Sluigi if (map) 207200855Sluigi free(map, M_IPFW); 208200590Sluigi return (0); 209200590Sluigi} 210200590Sluigi 211200590Sluigi/* 212200590Sluigi * Reclaim storage associated with a list of rules. This is 213200590Sluigi * typically the list created using remove_rule. 214200590Sluigi * A NULL pointer on input is handled correctly. 215200590Sluigi */ 216200590Sluigivoid 217200590Sluigiipfw_reap_rules(struct ip_fw *head) 218200590Sluigi{ 219200590Sluigi struct ip_fw *rule; 220200590Sluigi 221200590Sluigi while ((rule = head) != NULL) { 222200855Sluigi head = head->x_next; 223200590Sluigi free(rule, M_IPFW); 224200590Sluigi } 225200590Sluigi} 226200590Sluigi 227206339Sluigi/* 228206339Sluigi * Used by del_entry() to check if a rule should be kept. 229206339Sluigi * Returns 1 if the rule must be kept, 0 otherwise. 230206339Sluigi * 231206339Sluigi * Called with cmd = {0,1,5}. 232206339Sluigi * cmd == 0 matches on rule numbers, excludes rules in RESVD_SET if n == 0 ; 233206339Sluigi * cmd == 1 matches on set numbers only, rule numbers are ignored; 234206339Sluigi * cmd == 5 matches on rule and set numbers. 235206339Sluigi * 236206339Sluigi * n == 0 is a wildcard for rule numbers, there is no wildcard for sets. 237206339Sluigi * 238206339Sluigi * Rules to keep are 239206339Sluigi * (default || reserved || !match_set || !match_number) 240206339Sluigi * where 241206339Sluigi * default ::= (rule->rulenum == IPFW_DEFAULT_RULE) 242206339Sluigi * // the default rule is always protected 243206339Sluigi * 244206339Sluigi * reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET) 245206339Sluigi * // RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush") 246206339Sluigi * 247206339Sluigi * match_set ::= (cmd == 0 || rule->set == set) 248206339Sluigi * // set number is ignored for cmd == 0 249206339Sluigi * 250206339Sluigi * match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum) 251206339Sluigi * // number is ignored for cmd == 1 or n == 0 252206339Sluigi * 253206339Sluigi */ 254206339Sluigistatic int 255206339Sluigikeep_rule(struct ip_fw *rule, uint8_t cmd, uint8_t set, uint32_t n) 256206339Sluigi{ 257206339Sluigi return 258206339Sluigi (rule->rulenum == IPFW_DEFAULT_RULE) || 259206339Sluigi (cmd == 0 && n == 0 && rule->set == RESVD_SET) || 260206339Sluigi !(cmd == 0 || rule->set == set) || 261206339Sluigi !(cmd == 1 || n == 0 || n == rule->rulenum); 262206339Sluigi} 263206339Sluigi 264200590Sluigi/** 265206339Sluigi * Remove all rules with given number, or do set manipulation. 266200590Sluigi * Assumes chain != NULL && *chain != NULL. 267200590Sluigi * 268206339Sluigi * The argument is an uint32_t. The low 16 bit are the rule or set number; 269206339Sluigi * the next 8 bits are the new set; the top 8 bits indicate the command: 270200590Sluigi * 271205955Sluigi * 0 delete rules numbered "rulenum" 272205955Sluigi * 1 delete rules in set "rulenum" 273205955Sluigi * 2 move rules "rulenum" to set "new_set" 274205955Sluigi * 3 move rules from set "rulenum" to set "new_set" 275205955Sluigi * 4 swap sets "rulenum" and "new_set" 276205955Sluigi * 5 delete rules "rulenum" and set "new_set" 277200590Sluigi */ 278200590Sluigistatic int 279206339Sluigidel_entry(struct ip_fw_chain *chain, uint32_t arg) 280200590Sluigi{ 281200855Sluigi struct ip_fw *rule; 282206339Sluigi uint32_t num; /* rule number or old_set */ 283200855Sluigi uint8_t cmd, new_set; 284206339Sluigi int start, end, i, ofs, n; 285200855Sluigi struct ip_fw **map = NULL; 286200855Sluigi int error = 0; 287200590Sluigi 288206339Sluigi num = arg & 0xffff; 289200590Sluigi cmd = (arg >> 24) & 0xff; 290200590Sluigi new_set = (arg >> 16) & 0xff; 291200590Sluigi 292200590Sluigi if (cmd > 5 || new_set > RESVD_SET) 293200590Sluigi return EINVAL; 294200590Sluigi if (cmd == 0 || cmd == 2 || cmd == 5) { 295206339Sluigi if (num >= IPFW_DEFAULT_RULE) 296200590Sluigi return EINVAL; 297200590Sluigi } else { 298206339Sluigi if (num > RESVD_SET) /* old_set */ 299200590Sluigi return EINVAL; 300200590Sluigi } 301200590Sluigi 302205830Sluigi IPFW_UH_WLOCK(chain); /* arbitrate writers */ 303200590Sluigi chain->reap = NULL; /* prepare for deletions */ 304200855Sluigi 305200590Sluigi switch (cmd) { 306206339Sluigi case 0: /* delete rules "num" (num == 0 matches all) */ 307205830Sluigi case 1: /* delete all rules in set N */ 308205830Sluigi case 5: /* delete rules with number N and set "new_set". */ 309205830Sluigi 310205830Sluigi /* 311205830Sluigi * Locate first rule to delete (start), the rule after 312205830Sluigi * the last one to delete (end), and count how many 313206339Sluigi * rules to delete (n). Always use keep_rule() to 314206339Sluigi * determine which rules to keep. 315200590Sluigi */ 316200855Sluigi n = 0; 317206339Sluigi if (cmd == 1) { 318206339Sluigi /* look for a specific set including RESVD_SET. 319206339Sluigi * Must scan the entire range, ignore num. 320206339Sluigi */ 321206339Sluigi new_set = num; 322206339Sluigi for (start = -1, end = i = 0; i < chain->n_rules; i++) { 323206339Sluigi if (keep_rule(chain->map[i], cmd, new_set, 0)) 324200855Sluigi continue; 325200855Sluigi if (start < 0) 326200855Sluigi start = i; 327200855Sluigi end = i; 328200855Sluigi n++; 329200855Sluigi } 330200855Sluigi end++; /* first non-matching */ 331200855Sluigi } else { 332206339Sluigi /* Optimized search on rule numbers */ 333206339Sluigi start = ipfw_find_rule(chain, num, 0); 334200855Sluigi for (end = start; end < chain->n_rules; end++) { 335200855Sluigi rule = chain->map[end]; 336206339Sluigi if (num > 0 && rule->rulenum != num) 337200855Sluigi break; 338206339Sluigi if (!keep_rule(rule, cmd, new_set, num)) 339200855Sluigi n++; 340200855Sluigi } 341200590Sluigi } 342206339Sluigi 343206339Sluigi if (n == 0) { 344222742Sae /* A flush request (arg == 0 or cmd == 1) on empty 345222742Sae * ruleset returns with no error. On the contrary, 346206339Sluigi * if there is no match on a specific request, 347206339Sluigi * we return EINVAL. 348206339Sluigi */ 349222742Sae if (arg != 0 && cmd != 1) 350222742Sae error = EINVAL; 351206339Sluigi break; 352206339Sluigi } 353206339Sluigi 354206339Sluigi /* We have something to delete. Allocate the new map */ 355206339Sluigi map = get_map(chain, -n, 1 /* locked */); 356206339Sluigi if (map == NULL) { 357200855Sluigi error = EINVAL; 358200951Sluigi break; 359200855Sluigi } 360206339Sluigi 361206339Sluigi /* 1. bcopy the initial part of the map */ 362200855Sluigi if (start > 0) 363200855Sluigi bcopy(chain->map, map, start * sizeof(struct ip_fw *)); 364206339Sluigi /* 2. copy active rules between start and end */ 365200855Sluigi for (i = ofs = start; i < end; i++) { 366200855Sluigi rule = chain->map[i]; 367206339Sluigi if (keep_rule(rule, cmd, new_set, num)) 368206339Sluigi map[ofs++] = rule; 369200855Sluigi } 370206339Sluigi /* 3. copy the final part of the map */ 371200855Sluigi bcopy(chain->map + end, map + ofs, 372200855Sluigi (chain->n_rules - end) * sizeof(struct ip_fw *)); 373206339Sluigi /* 4. swap the maps (under BH_LOCK) */ 374200855Sluigi map = swap_map(chain, map, chain->n_rules - n); 375206339Sluigi /* 5. now remove the rules deleted from the old map */ 376248697Sae if (cmd == 1) 377248697Sae ipfw_expire_dyn_rules(chain, NULL, new_set); 378200855Sluigi for (i = start; i < end; i++) { 379200855Sluigi rule = map[i]; 380206339Sluigi if (keep_rule(rule, cmd, new_set, num)) 381205830Sluigi continue; 382248697Sae chain->static_len -= RULESIZE(rule); 383248697Sae if (cmd != 1) 384248697Sae ipfw_expire_dyn_rules(chain, rule, RESVD_SET); 385205830Sluigi rule->x_next = chain->reap; 386205830Sluigi chain->reap = rule; 387200590Sluigi } 388200590Sluigi break; 389200590Sluigi 390206339Sluigi /* 391206339Sluigi * In the next 3 cases the loop stops at (n_rules - 1) 392206339Sluigi * because the default rule is never eligible.. 393206339Sluigi */ 394206339Sluigi 395206339Sluigi case 2: /* move rules with given RULE number to new set */ 396206339Sluigi for (i = 0; i < chain->n_rules - 1; i++) { 397200855Sluigi rule = chain->map[i]; 398206339Sluigi if (rule->rulenum == num) 399200590Sluigi rule->set = new_set; 400200855Sluigi } 401200590Sluigi break; 402200590Sluigi 403206339Sluigi case 3: /* move rules with given SET number to new set */ 404206339Sluigi for (i = 0; i < chain->n_rules - 1; i++) { 405200855Sluigi rule = chain->map[i]; 406206339Sluigi if (rule->set == num) 407200590Sluigi rule->set = new_set; 408200855Sluigi } 409200590Sluigi break; 410200590Sluigi 411200590Sluigi case 4: /* swap two sets */ 412206339Sluigi for (i = 0; i < chain->n_rules - 1; i++) { 413200855Sluigi rule = chain->map[i]; 414206339Sluigi if (rule->set == num) 415200590Sluigi rule->set = new_set; 416200590Sluigi else if (rule->set == new_set) 417206339Sluigi rule->set = num; 418200855Sluigi } 419200590Sluigi break; 420200590Sluigi } 421206339Sluigi 422200590Sluigi rule = chain->reap; 423200855Sluigi chain->reap = NULL; 424200855Sluigi IPFW_UH_WUNLOCK(chain); 425200590Sluigi ipfw_reap_rules(rule); 426200855Sluigi if (map) 427200855Sluigi free(map, M_IPFW); 428200855Sluigi return error; 429200590Sluigi} 430200590Sluigi 431200590Sluigi/* 432200590Sluigi * Clear counters for a specific rule. 433200855Sluigi * Normally run under IPFW_UH_RLOCK, but these are idempotent ops 434200855Sluigi * so we only care that rules do not disappear. 435200590Sluigi */ 436200590Sluigistatic void 437200590Sluigiclear_counters(struct ip_fw *rule, int log_only) 438200590Sluigi{ 439200590Sluigi ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule); 440200590Sluigi 441243711Smelifaro if (log_only == 0) 442243711Smelifaro IPFW_ZERO_RULE_COUNTER(rule); 443200590Sluigi if (l->o.opcode == O_LOG) 444200590Sluigi l->log_left = l->max_log; 445200590Sluigi} 446200590Sluigi 447200590Sluigi/** 448200590Sluigi * Reset some or all counters on firewall rules. 449200590Sluigi * The argument `arg' is an u_int32_t. The low 16 bit are the rule number, 450200590Sluigi * the next 8 bits are the set number, the top 8 bits are the command: 451200590Sluigi * 0 work with rules from all set's; 452200590Sluigi * 1 work with rules only from specified set. 453200590Sluigi * Specified rule number is zero if we want to clear all entries. 454200590Sluigi * log_only is 1 if we only want to reset logs, zero otherwise. 455200590Sluigi */ 456200590Sluigistatic int 457200590Sluigizero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only) 458200590Sluigi{ 459200590Sluigi struct ip_fw *rule; 460200590Sluigi char *msg; 461200855Sluigi int i; 462200590Sluigi 463200590Sluigi uint16_t rulenum = arg & 0xffff; 464200590Sluigi uint8_t set = (arg >> 16) & 0xff; 465200590Sluigi uint8_t cmd = (arg >> 24) & 0xff; 466200590Sluigi 467200590Sluigi if (cmd > 1) 468200590Sluigi return (EINVAL); 469200590Sluigi if (cmd == 1 && set > RESVD_SET) 470200590Sluigi return (EINVAL); 471200590Sluigi 472200855Sluigi IPFW_UH_RLOCK(chain); 473200590Sluigi if (rulenum == 0) { 474200590Sluigi V_norule_counter = 0; 475200855Sluigi for (i = 0; i < chain->n_rules; i++) { 476200855Sluigi rule = chain->map[i]; 477200855Sluigi /* Skip rules not in our set. */ 478200590Sluigi if (cmd == 1 && rule->set != set) 479200590Sluigi continue; 480200590Sluigi clear_counters(rule, log_only); 481200590Sluigi } 482200590Sluigi msg = log_only ? "All logging counts reset" : 483200590Sluigi "Accounting cleared"; 484200590Sluigi } else { 485200590Sluigi int cleared = 0; 486200855Sluigi for (i = 0; i < chain->n_rules; i++) { 487200855Sluigi rule = chain->map[i]; 488200590Sluigi if (rule->rulenum == rulenum) { 489200951Sluigi if (cmd == 0 || rule->set == set) 490200951Sluigi clear_counters(rule, log_only); 491200590Sluigi cleared = 1; 492200855Sluigi } 493200855Sluigi if (rule->rulenum > rulenum) 494200590Sluigi break; 495200951Sluigi } 496200590Sluigi if (!cleared) { /* we did not find any matching rules */ 497205830Sluigi IPFW_UH_RUNLOCK(chain); 498200590Sluigi return (EINVAL); 499200590Sluigi } 500200590Sluigi msg = log_only ? "logging count reset" : "cleared"; 501200590Sluigi } 502200855Sluigi IPFW_UH_RUNLOCK(chain); 503200590Sluigi 504200590Sluigi if (V_fw_verbose) { 505200590Sluigi int lev = LOG_SECURITY | LOG_NOTICE; 506200590Sluigi 507200590Sluigi if (rulenum) 508200590Sluigi log(lev, "ipfw: Entry %d %s.\n", rulenum, msg); 509200590Sluigi else 510200590Sluigi log(lev, "ipfw: %s.\n", msg); 511200590Sluigi } 512200590Sluigi return (0); 513200590Sluigi} 514200590Sluigi 515200590Sluigi/* 516200590Sluigi * Check validity of the structure before insert. 517200601Sluigi * Rules are simple, so this mostly need to check rule sizes. 518200590Sluigi */ 519200590Sluigistatic int 520200590Sluigicheck_ipfw_struct(struct ip_fw *rule, int size) 521200590Sluigi{ 522200590Sluigi int l, cmdlen = 0; 523200590Sluigi int have_action=0; 524200590Sluigi ipfw_insn *cmd; 525200590Sluigi 526200590Sluigi if (size < sizeof(*rule)) { 527200590Sluigi printf("ipfw: rule too short\n"); 528200590Sluigi return (EINVAL); 529200590Sluigi } 530200590Sluigi /* first, check for valid size */ 531200590Sluigi l = RULESIZE(rule); 532200590Sluigi if (l != size) { 533200590Sluigi printf("ipfw: size mismatch (have %d want %d)\n", size, l); 534200590Sluigi return (EINVAL); 535200590Sluigi } 536200590Sluigi if (rule->act_ofs >= rule->cmd_len) { 537200590Sluigi printf("ipfw: bogus action offset (%u > %u)\n", 538200590Sluigi rule->act_ofs, rule->cmd_len - 1); 539200590Sluigi return (EINVAL); 540200590Sluigi } 541200590Sluigi /* 542200590Sluigi * Now go for the individual checks. Very simple ones, basically only 543200590Sluigi * instruction sizes. 544200590Sluigi */ 545200590Sluigi for (l = rule->cmd_len, cmd = rule->cmd ; 546200590Sluigi l > 0 ; l -= cmdlen, cmd += cmdlen) { 547200590Sluigi cmdlen = F_LEN(cmd); 548200590Sluigi if (cmdlen > l) { 549200590Sluigi printf("ipfw: opcode %d size truncated\n", 550200590Sluigi cmd->opcode); 551200590Sluigi return EINVAL; 552200590Sluigi } 553200590Sluigi switch (cmd->opcode) { 554200590Sluigi case O_PROBE_STATE: 555200590Sluigi case O_KEEP_STATE: 556200590Sluigi case O_PROTO: 557200590Sluigi case O_IP_SRC_ME: 558200590Sluigi case O_IP_DST_ME: 559200590Sluigi case O_LAYER2: 560200590Sluigi case O_IN: 561200590Sluigi case O_FRAG: 562200590Sluigi case O_DIVERTED: 563200590Sluigi case O_IPOPT: 564200590Sluigi case O_IPTOS: 565200590Sluigi case O_IPPRECEDENCE: 566200590Sluigi case O_IPVER: 567215179Sluigi case O_SOCKARG: 568200590Sluigi case O_TCPFLAGS: 569200590Sluigi case O_TCPOPTS: 570200590Sluigi case O_ESTAB: 571200590Sluigi case O_VERREVPATH: 572200590Sluigi case O_VERSRCREACH: 573200590Sluigi case O_ANTISPOOF: 574200590Sluigi case O_IPSEC: 575200590Sluigi#ifdef INET6 576200590Sluigi case O_IP6_SRC_ME: 577200590Sluigi case O_IP6_DST_ME: 578200590Sluigi case O_EXT_HDR: 579200590Sluigi case O_IP6: 580200590Sluigi#endif 581200590Sluigi case O_IP4: 582200590Sluigi case O_TAG: 583200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn)) 584200590Sluigi goto bad_size; 585200590Sluigi break; 586200590Sluigi 587200590Sluigi case O_FIB: 588200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn)) 589200590Sluigi goto bad_size; 590200590Sluigi if (cmd->arg1 >= rt_numfibs) { 591200590Sluigi printf("ipfw: invalid fib number %d\n", 592200590Sluigi cmd->arg1); 593200590Sluigi return EINVAL; 594200590Sluigi } 595200590Sluigi break; 596200590Sluigi 597200590Sluigi case O_SETFIB: 598200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn)) 599200590Sluigi goto bad_size; 600222474Sae if ((cmd->arg1 != IP_FW_TABLEARG) && 601222474Sae (cmd->arg1 >= rt_numfibs)) { 602200590Sluigi printf("ipfw: invalid fib number %d\n", 603200590Sluigi cmd->arg1); 604200590Sluigi return EINVAL; 605200590Sluigi } 606200590Sluigi goto check_action; 607200590Sluigi 608200590Sluigi case O_UID: 609200590Sluigi case O_GID: 610200590Sluigi case O_JAIL: 611200590Sluigi case O_IP_SRC: 612200590Sluigi case O_IP_DST: 613200590Sluigi case O_TCPSEQ: 614200590Sluigi case O_TCPACK: 615200590Sluigi case O_PROB: 616200590Sluigi case O_ICMPTYPE: 617200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 618200590Sluigi goto bad_size; 619200590Sluigi break; 620200590Sluigi 621200590Sluigi case O_LIMIT: 622200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) 623200590Sluigi goto bad_size; 624200590Sluigi break; 625200590Sluigi 626200590Sluigi case O_LOG: 627200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) 628200590Sluigi goto bad_size; 629200590Sluigi 630200590Sluigi ((ipfw_insn_log *)cmd)->log_left = 631200590Sluigi ((ipfw_insn_log *)cmd)->max_log; 632200590Sluigi 633200590Sluigi break; 634200590Sluigi 635200590Sluigi case O_IP_SRC_MASK: 636200590Sluigi case O_IP_DST_MASK: 637200590Sluigi /* only odd command lengths */ 638200590Sluigi if ( !(cmdlen & 1) || cmdlen > 31) 639200590Sluigi goto bad_size; 640200590Sluigi break; 641200590Sluigi 642200590Sluigi case O_IP_SRC_SET: 643200590Sluigi case O_IP_DST_SET: 644200590Sluigi if (cmd->arg1 == 0 || cmd->arg1 > 256) { 645200590Sluigi printf("ipfw: invalid set size %d\n", 646200590Sluigi cmd->arg1); 647200590Sluigi return EINVAL; 648200590Sluigi } 649200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 650200590Sluigi (cmd->arg1+31)/32 ) 651200590Sluigi goto bad_size; 652200590Sluigi break; 653200590Sluigi 654200590Sluigi case O_IP_SRC_LOOKUP: 655200590Sluigi case O_IP_DST_LOOKUP: 656200590Sluigi if (cmd->arg1 >= IPFW_TABLES_MAX) { 657200590Sluigi printf("ipfw: invalid table number %d\n", 658200590Sluigi cmd->arg1); 659200590Sluigi return (EINVAL); 660200590Sluigi } 661200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn) && 662200590Sluigi cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && 663200590Sluigi cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 664200590Sluigi goto bad_size; 665200590Sluigi break; 666200590Sluigi case O_MACADDR2: 667200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) 668200590Sluigi goto bad_size; 669200590Sluigi break; 670200590Sluigi 671200590Sluigi case O_NOP: 672200590Sluigi case O_IPID: 673200590Sluigi case O_IPTTL: 674200590Sluigi case O_IPLEN: 675200590Sluigi case O_TCPDATALEN: 676231076Sglebius case O_TCPWIN: 677200590Sluigi case O_TAGGED: 678200590Sluigi if (cmdlen < 1 || cmdlen > 31) 679200590Sluigi goto bad_size; 680200590Sluigi break; 681200590Sluigi 682248971Smelifaro case O_DSCP: 683248971Smelifaro if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1) 684248971Smelifaro goto bad_size; 685248971Smelifaro break; 686248971Smelifaro 687200590Sluigi case O_MAC_TYPE: 688200590Sluigi case O_IP_SRCPORT: 689200590Sluigi case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ 690200590Sluigi if (cmdlen < 2 || cmdlen > 31) 691200590Sluigi goto bad_size; 692200590Sluigi break; 693200590Sluigi 694200590Sluigi case O_RECV: 695200590Sluigi case O_XMIT: 696200590Sluigi case O_VIA: 697200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) 698200590Sluigi goto bad_size; 699200590Sluigi break; 700200590Sluigi 701200590Sluigi case O_ALTQ: 702200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) 703200590Sluigi goto bad_size; 704200590Sluigi break; 705200590Sluigi 706200590Sluigi case O_PIPE: 707200590Sluigi case O_QUEUE: 708200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn)) 709200590Sluigi goto bad_size; 710200590Sluigi goto check_action; 711200590Sluigi 712200590Sluigi case O_FORWARD_IP: 713200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) 714200590Sluigi goto bad_size; 715200590Sluigi goto check_action; 716225044Sbz#ifdef INET6 717225044Sbz case O_FORWARD_IP6: 718225044Sbz if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6)) 719225044Sbz goto bad_size; 720225044Sbz goto check_action; 721225044Sbz#endif /* INET6 */ 722225044Sbz 723200590Sluigi case O_DIVERT: 724200590Sluigi case O_TEE: 725200590Sluigi if (ip_divert_ptr == NULL) 726200590Sluigi return EINVAL; 727200590Sluigi else 728200590Sluigi goto check_size; 729200590Sluigi case O_NETGRAPH: 730200590Sluigi case O_NGTEE: 731201732Sluigi if (ng_ipfw_input_p == NULL) 732200590Sluigi return EINVAL; 733200590Sluigi else 734200590Sluigi goto check_size; 735200590Sluigi case O_NAT: 736200590Sluigi if (!IPFW_NAT_LOADED) 737200590Sluigi return EINVAL; 738200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) 739200590Sluigi goto bad_size; 740200590Sluigi goto check_action; 741200590Sluigi case O_FORWARD_MAC: /* XXX not implemented yet */ 742200590Sluigi case O_CHECK_STATE: 743200590Sluigi case O_COUNT: 744200590Sluigi case O_ACCEPT: 745200590Sluigi case O_DENY: 746200590Sluigi case O_REJECT: 747248552Smelifaro case O_SETDSCP: 748200590Sluigi#ifdef INET6 749200590Sluigi case O_UNREACH6: 750200590Sluigi#endif 751200590Sluigi case O_SKIPTO: 752200590Sluigi case O_REASS: 753223666Sae case O_CALLRETURN: 754200590Sluigicheck_size: 755200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn)) 756200590Sluigi goto bad_size; 757200590Sluigicheck_action: 758200590Sluigi if (have_action) { 759200590Sluigi printf("ipfw: opcode %d, multiple actions" 760200590Sluigi " not allowed\n", 761200590Sluigi cmd->opcode); 762200590Sluigi return EINVAL; 763200590Sluigi } 764200590Sluigi have_action = 1; 765200590Sluigi if (l != cmdlen) { 766200590Sluigi printf("ipfw: opcode %d, action must be" 767200590Sluigi " last opcode\n", 768200590Sluigi cmd->opcode); 769200590Sluigi return EINVAL; 770200590Sluigi } 771200590Sluigi break; 772200590Sluigi#ifdef INET6 773200590Sluigi case O_IP6_SRC: 774200590Sluigi case O_IP6_DST: 775200590Sluigi if (cmdlen != F_INSN_SIZE(struct in6_addr) + 776200590Sluigi F_INSN_SIZE(ipfw_insn)) 777200590Sluigi goto bad_size; 778200590Sluigi break; 779200590Sluigi 780200590Sluigi case O_FLOW6ID: 781200590Sluigi if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 782200590Sluigi ((ipfw_insn_u32 *)cmd)->o.arg1) 783200590Sluigi goto bad_size; 784200590Sluigi break; 785200590Sluigi 786200590Sluigi case O_IP6_SRC_MASK: 787200590Sluigi case O_IP6_DST_MASK: 788200590Sluigi if ( !(cmdlen & 1) || cmdlen > 127) 789200590Sluigi goto bad_size; 790200590Sluigi break; 791200590Sluigi case O_ICMP6TYPE: 792200590Sluigi if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) 793200590Sluigi goto bad_size; 794200590Sluigi break; 795200590Sluigi#endif 796200590Sluigi 797200590Sluigi default: 798200590Sluigi switch (cmd->opcode) { 799200590Sluigi#ifndef INET6 800200590Sluigi case O_IP6_SRC_ME: 801200590Sluigi case O_IP6_DST_ME: 802200590Sluigi case O_EXT_HDR: 803200590Sluigi case O_IP6: 804200590Sluigi case O_UNREACH6: 805200590Sluigi case O_IP6_SRC: 806200590Sluigi case O_IP6_DST: 807200590Sluigi case O_FLOW6ID: 808200590Sluigi case O_IP6_SRC_MASK: 809200590Sluigi case O_IP6_DST_MASK: 810200590Sluigi case O_ICMP6TYPE: 811200590Sluigi printf("ipfw: no IPv6 support in kernel\n"); 812200590Sluigi return EPROTONOSUPPORT; 813200590Sluigi#endif 814200590Sluigi default: 815200590Sluigi printf("ipfw: opcode %d, unknown opcode\n", 816200590Sluigi cmd->opcode); 817200590Sluigi return EINVAL; 818200590Sluigi } 819200590Sluigi } 820200590Sluigi } 821200590Sluigi if (have_action == 0) { 822200590Sluigi printf("ipfw: missing action\n"); 823200590Sluigi return EINVAL; 824200590Sluigi } 825200590Sluigi return 0; 826200590Sluigi 827200590Sluigibad_size: 828200590Sluigi printf("ipfw: opcode %d size %d wrong\n", 829200590Sluigi cmd->opcode, cmdlen); 830200590Sluigi return EINVAL; 831200590Sluigi} 832200590Sluigi 833204591Sluigi 834200590Sluigi/* 835204591Sluigi * Translation of requests for compatibility with FreeBSD 7.2/8. 836204591Sluigi * a static variable tells us if we have an old client from userland, 837204591Sluigi * and if necessary we translate requests and responses between the 838204591Sluigi * two formats. 839204591Sluigi */ 840204591Sluigistatic int is7 = 0; 841204591Sluigi 842204591Sluigistruct ip_fw7 { 843204591Sluigi struct ip_fw7 *next; /* linked list of rules */ 844204591Sluigi struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */ 845204591Sluigi /* 'next_rule' is used to pass up 'set_disable' status */ 846204591Sluigi 847204591Sluigi uint16_t act_ofs; /* offset of action in 32-bit units */ 848204591Sluigi uint16_t cmd_len; /* # of 32-bit words in cmd */ 849204591Sluigi uint16_t rulenum; /* rule number */ 850204591Sluigi uint8_t set; /* rule set (0..31) */ 851204591Sluigi // #define RESVD_SET 31 /* set for default and persistent rules */ 852204591Sluigi uint8_t _pad; /* padding */ 853204591Sluigi // uint32_t id; /* rule id, only in v.8 */ 854204591Sluigi /* These fields are present in all rules. */ 855204591Sluigi uint64_t pcnt; /* Packet counter */ 856204591Sluigi uint64_t bcnt; /* Byte counter */ 857204591Sluigi uint32_t timestamp; /* tv_sec of last match */ 858204591Sluigi 859204591Sluigi ipfw_insn cmd[1]; /* storage for commands */ 860204591Sluigi}; 861204591Sluigi 862204591Sluigi int convert_rule_to_7(struct ip_fw *rule); 863204591Sluigiint convert_rule_to_8(struct ip_fw *rule); 864204591Sluigi 865204591Sluigi#ifndef RULESIZE7 866204591Sluigi#define RULESIZE7(rule) (sizeof(struct ip_fw7) + \ 867204591Sluigi ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4) 868204591Sluigi#endif 869204591Sluigi 870204591Sluigi 871204591Sluigi/* 872200590Sluigi * Copy the static and dynamic rules to the supplied buffer 873200590Sluigi * and return the amount of space actually used. 874200855Sluigi * Must be run under IPFW_UH_RLOCK 875200590Sluigi */ 876200590Sluigistatic size_t 877200590Sluigiipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) 878200590Sluigi{ 879200590Sluigi char *bp = buf; 880200590Sluigi char *ep = bp + space; 881200855Sluigi struct ip_fw *rule, *dst; 882200855Sluigi int l, i; 883200590Sluigi time_t boot_seconds; 884200590Sluigi 885200590Sluigi boot_seconds = boottime.tv_sec; 886200855Sluigi for (i = 0; i < chain->n_rules; i++) { 887200855Sluigi rule = chain->map[i]; 888204591Sluigi 889204591Sluigi if (is7) { 890204591Sluigi /* Convert rule to FreeBSd 7.2 format */ 891204591Sluigi l = RULESIZE7(rule); 892204591Sluigi if (bp + l + sizeof(uint32_t) <= ep) { 893204591Sluigi int error; 894204591Sluigi bcopy(rule, bp, l + sizeof(uint32_t)); 895204591Sluigi error = convert_rule_to_7((struct ip_fw *) bp); 896204591Sluigi if (error) 897204591Sluigi return 0; /*XXX correct? */ 898204591Sluigi /* 899204591Sluigi * XXX HACK. Store the disable mask in the "next" 900204591Sluigi * pointer in a wild attempt to keep the ABI the same. 901204591Sluigi * Why do we do this on EVERY rule? 902204591Sluigi */ 903204591Sluigi bcopy(&V_set_disable, 904204591Sluigi &(((struct ip_fw7 *)bp)->next_rule), 905204591Sluigi sizeof(V_set_disable)); 906204591Sluigi if (((struct ip_fw7 *)bp)->timestamp) 907204591Sluigi ((struct ip_fw7 *)bp)->timestamp += boot_seconds; 908204591Sluigi bp += l; 909204591Sluigi } 910204591Sluigi continue; /* go to next rule */ 911204591Sluigi } 912204591Sluigi 913204591Sluigi /* normal mode, don't touch rules */ 914200855Sluigi l = RULESIZE(rule); 915200855Sluigi if (bp + l > ep) { /* should not happen */ 916200855Sluigi printf("overflow dumping static rules\n"); 917200855Sluigi break; 918200855Sluigi } 919200855Sluigi dst = (struct ip_fw *)bp; 920200855Sluigi bcopy(rule, dst, l); 921200951Sluigi /* 922200951Sluigi * XXX HACK. Store the disable mask in the "next" 923200951Sluigi * pointer in a wild attempt to keep the ABI the same. 924200951Sluigi * Why do we do this on EVERY rule? 925200951Sluigi */ 926200855Sluigi bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable)); 927200855Sluigi if (dst->timestamp) 928200855Sluigi dst->timestamp += boot_seconds; 929200855Sluigi bp += l; 930200951Sluigi } 931243707Smelifaro ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */ 932200590Sluigi return (bp - (char *)buf); 933200590Sluigi} 934200590Sluigi 935200590Sluigi 936232865Smelifaro#define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader)) 937200590Sluigi/** 938200590Sluigi * {set|get}sockopt parser. 939200590Sluigi */ 940200590Sluigiint 941200590Sluigiipfw_ctl(struct sockopt *sopt) 942200590Sluigi{ 943200590Sluigi#define RULE_MAXSIZE (256*sizeof(u_int32_t)) 944200590Sluigi int error; 945232865Smelifaro size_t size, len, valsize; 946200590Sluigi struct ip_fw *buf, *rule; 947200838Sluigi struct ip_fw_chain *chain; 948200590Sluigi u_int32_t rulenum[2]; 949232865Smelifaro uint32_t opt; 950232865Smelifaro char xbuf[128]; 951232865Smelifaro ip_fw3_opheader *op3 = NULL; 952200590Sluigi 953200590Sluigi error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); 954200590Sluigi if (error) 955200590Sluigi return (error); 956200590Sluigi 957200590Sluigi /* 958200590Sluigi * Disallow modifications in really-really secure mode, but still allow 959200590Sluigi * the logging counters to be reset. 960200590Sluigi */ 961200590Sluigi if (sopt->sopt_name == IP_FW_ADD || 962200590Sluigi (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) { 963200590Sluigi error = securelevel_ge(sopt->sopt_td->td_ucred, 3); 964200590Sluigi if (error) 965200590Sluigi return (error); 966200590Sluigi } 967200590Sluigi 968200838Sluigi chain = &V_layer3_chain; 969200590Sluigi error = 0; 970200590Sluigi 971232865Smelifaro /* Save original valsize before it is altered via sooptcopyin() */ 972232865Smelifaro valsize = sopt->sopt_valsize; 973232865Smelifaro if ((opt = sopt->sopt_name) == IP_FW3) { 974232865Smelifaro /* 975232865Smelifaro * Copy not less than sizeof(ip_fw3_opheader). 976232865Smelifaro * We hope any IP_FW3 command will fit into 128-byte buffer. 977232865Smelifaro */ 978232865Smelifaro if ((error = sooptcopyin(sopt, xbuf, sizeof(xbuf), 979232865Smelifaro sizeof(ip_fw3_opheader))) != 0) 980232865Smelifaro return (error); 981232865Smelifaro op3 = (ip_fw3_opheader *)xbuf; 982232865Smelifaro opt = op3->opcode; 983232865Smelifaro } 984232865Smelifaro 985232865Smelifaro switch (opt) { 986200590Sluigi case IP_FW_GET: 987200590Sluigi /* 988200590Sluigi * pass up a copy of the current rules. Static rules 989200590Sluigi * come first (the last of which has number IPFW_DEFAULT_RULE), 990200590Sluigi * followed by a possibly empty list of dynamic rule. 991200590Sluigi * The last dynamic rule has NULL in the "next" field. 992200590Sluigi * 993200590Sluigi * Note that the calculated size is used to bound the 994200590Sluigi * amount of data returned to the user. The rule set may 995200590Sluigi * change between calculating the size and returning the 996200590Sluigi * data in which case we'll just return what fits. 997200590Sluigi */ 998200855Sluigi for (;;) { 999200855Sluigi int len = 0, want; 1000200590Sluigi 1001200855Sluigi size = chain->static_len; 1002200855Sluigi size += ipfw_dyn_len(); 1003200951Sluigi if (size >= sopt->sopt_valsize) 1004200951Sluigi break; 1005200951Sluigi buf = malloc(size, M_TEMP, M_WAITOK); 1006200855Sluigi IPFW_UH_RLOCK(chain); 1007200855Sluigi /* check again how much space we need */ 1008200855Sluigi want = chain->static_len + ipfw_dyn_len(); 1009200855Sluigi if (size >= want) 1010200855Sluigi len = ipfw_getrules(chain, buf, size); 1011200855Sluigi IPFW_UH_RUNLOCK(chain); 1012200855Sluigi if (size >= want) 1013200855Sluigi error = sooptcopyout(sopt, buf, len); 1014200951Sluigi free(buf, M_TEMP); 1015200855Sluigi if (size >= want) 1016200855Sluigi break; 1017200855Sluigi } 1018200590Sluigi break; 1019200590Sluigi 1020200590Sluigi case IP_FW_FLUSH: 1021200855Sluigi /* locking is done within del_entry() */ 1022200855Sluigi error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ 1023200590Sluigi break; 1024200590Sluigi 1025200590Sluigi case IP_FW_ADD: 1026200590Sluigi rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); 1027200590Sluigi error = sooptcopyin(sopt, rule, RULE_MAXSIZE, 1028204954Sluigi sizeof(struct ip_fw7) ); 1029204591Sluigi 1030204591Sluigi /* 1031204591Sluigi * If the size of commands equals RULESIZE7 then we assume 1032204591Sluigi * a FreeBSD7.2 binary is talking to us (set is7=1). 1033204591Sluigi * is7 is persistent so the next 'ipfw list' command 1034204591Sluigi * will use this format. 1035204591Sluigi * NOTE: If wrong version is guessed (this can happen if 1036204591Sluigi * the first ipfw command is 'ipfw [pipe] list') 1037204591Sluigi * the ipfw binary may crash or loop infinitly... 1038204591Sluigi */ 1039204591Sluigi if (sopt->sopt_valsize == RULESIZE7(rule)) { 1040204591Sluigi is7 = 1; 1041204591Sluigi error = convert_rule_to_8(rule); 1042204591Sluigi if (error) 1043204591Sluigi return error; 1044204591Sluigi if (error == 0) 1045204591Sluigi error = check_ipfw_struct(rule, RULESIZE(rule)); 1046204591Sluigi } else { 1047204591Sluigi is7 = 0; 1048200590Sluigi if (error == 0) 1049200590Sluigi error = check_ipfw_struct(rule, sopt->sopt_valsize); 1050204591Sluigi } 1051200590Sluigi if (error == 0) { 1052200855Sluigi /* locking is done within ipfw_add_rule() */ 1053200838Sluigi error = ipfw_add_rule(chain, rule); 1054200590Sluigi size = RULESIZE(rule); 1055204591Sluigi if (!error && sopt->sopt_dir == SOPT_GET) { 1056204591Sluigi if (is7) { 1057204591Sluigi error = convert_rule_to_7(rule); 1058204591Sluigi size = RULESIZE7(rule); 1059204591Sluigi if (error) 1060204591Sluigi return error; 1061204591Sluigi } 1062200590Sluigi error = sooptcopyout(sopt, rule, size); 1063200590Sluigi } 1064204591Sluigi } 1065200590Sluigi free(rule, M_TEMP); 1066200590Sluigi break; 1067200590Sluigi 1068200590Sluigi case IP_FW_DEL: 1069200590Sluigi /* 1070200590Sluigi * IP_FW_DEL is used for deleting single rules or sets, 1071200590Sluigi * and (ab)used to atomically manipulate sets. Argument size 1072200590Sluigi * is used to distinguish between the two: 1073200590Sluigi * sizeof(u_int32_t) 1074200590Sluigi * delete single rule or set of rules, 1075200590Sluigi * or reassign rules (or sets) to a different set. 1076200590Sluigi * 2*sizeof(u_int32_t) 1077200590Sluigi * atomic disable/enable sets. 1078200590Sluigi * first u_int32_t contains sets to be disabled, 1079200590Sluigi * second u_int32_t contains sets to be enabled. 1080200590Sluigi */ 1081200590Sluigi error = sooptcopyin(sopt, rulenum, 1082200590Sluigi 2*sizeof(u_int32_t), sizeof(u_int32_t)); 1083200590Sluigi if (error) 1084200590Sluigi break; 1085200590Sluigi size = sopt->sopt_valsize; 1086200838Sluigi if (size == sizeof(u_int32_t) && rulenum[0] != 0) { 1087200838Sluigi /* delete or reassign, locking done in del_entry() */ 1088200838Sluigi error = del_entry(chain, rulenum[0]); 1089200838Sluigi } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */ 1090200855Sluigi IPFW_UH_WLOCK(chain); 1091200590Sluigi V_set_disable = 1092200590Sluigi (V_set_disable | rulenum[0]) & ~rulenum[1] & 1093200590Sluigi ~(1<<RESVD_SET); /* set RESVD_SET always enabled */ 1094200855Sluigi IPFW_UH_WUNLOCK(chain); 1095200838Sluigi } else 1096200590Sluigi error = EINVAL; 1097200590Sluigi break; 1098200590Sluigi 1099200590Sluigi case IP_FW_ZERO: 1100200590Sluigi case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */ 1101200590Sluigi rulenum[0] = 0; 1102200590Sluigi if (sopt->sopt_val != 0) { 1103200590Sluigi error = sooptcopyin(sopt, rulenum, 1104200590Sluigi sizeof(u_int32_t), sizeof(u_int32_t)); 1105200590Sluigi if (error) 1106200590Sluigi break; 1107200590Sluigi } 1108200838Sluigi error = zero_entry(chain, rulenum[0], 1109200590Sluigi sopt->sopt_name == IP_FW_RESETLOG); 1110200590Sluigi break; 1111200590Sluigi 1112200838Sluigi /*--- TABLE manipulations are protected by the IPFW_LOCK ---*/ 1113200590Sluigi case IP_FW_TABLE_ADD: 1114200590Sluigi { 1115200590Sluigi ipfw_table_entry ent; 1116200590Sluigi 1117200590Sluigi error = sooptcopyin(sopt, &ent, 1118200590Sluigi sizeof(ent), sizeof(ent)); 1119200590Sluigi if (error) 1120200590Sluigi break; 1121200838Sluigi error = ipfw_add_table_entry(chain, ent.tbl, 1122232865Smelifaro &ent.addr, sizeof(ent.addr), ent.masklen, 1123232865Smelifaro IPFW_TABLE_CIDR, ent.value); 1124200590Sluigi } 1125200590Sluigi break; 1126200590Sluigi 1127200590Sluigi case IP_FW_TABLE_DEL: 1128200590Sluigi { 1129200590Sluigi ipfw_table_entry ent; 1130200590Sluigi 1131200590Sluigi error = sooptcopyin(sopt, &ent, 1132200590Sluigi sizeof(ent), sizeof(ent)); 1133200590Sluigi if (error) 1134200590Sluigi break; 1135200838Sluigi error = ipfw_del_table_entry(chain, ent.tbl, 1136232865Smelifaro &ent.addr, sizeof(ent.addr), ent.masklen, IPFW_TABLE_CIDR); 1137200590Sluigi } 1138200590Sluigi break; 1139200590Sluigi 1140232865Smelifaro case IP_FW_TABLE_XADD: /* IP_FW3 */ 1141232865Smelifaro case IP_FW_TABLE_XDEL: /* IP_FW3 */ 1142232865Smelifaro { 1143232865Smelifaro ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); 1144232865Smelifaro 1145232865Smelifaro /* Check minimum header size */ 1146232865Smelifaro if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { 1147232865Smelifaro error = EINVAL; 1148232865Smelifaro break; 1149232865Smelifaro } 1150232865Smelifaro 1151232865Smelifaro /* Check if len field is valid */ 1152232865Smelifaro if (xent->len > sizeof(ipfw_table_xentry)) { 1153232865Smelifaro error = EINVAL; 1154232865Smelifaro break; 1155232865Smelifaro } 1156232865Smelifaro 1157232865Smelifaro len = xent->len - offsetof(ipfw_table_xentry, k); 1158232865Smelifaro 1159232865Smelifaro error = (opt == IP_FW_TABLE_XADD) ? 1160232865Smelifaro ipfw_add_table_entry(chain, xent->tbl, &xent->k, 1161232865Smelifaro len, xent->masklen, xent->type, xent->value) : 1162232865Smelifaro ipfw_del_table_entry(chain, xent->tbl, &xent->k, 1163232865Smelifaro len, xent->masklen, xent->type); 1164232865Smelifaro } 1165232865Smelifaro break; 1166232865Smelifaro 1167200590Sluigi case IP_FW_TABLE_FLUSH: 1168200590Sluigi { 1169200590Sluigi u_int16_t tbl; 1170200590Sluigi 1171200590Sluigi error = sooptcopyin(sopt, &tbl, 1172200590Sluigi sizeof(tbl), sizeof(tbl)); 1173200590Sluigi if (error) 1174200590Sluigi break; 1175200838Sluigi error = ipfw_flush_table(chain, tbl); 1176200590Sluigi } 1177200590Sluigi break; 1178200590Sluigi 1179200590Sluigi case IP_FW_TABLE_GETSIZE: 1180200590Sluigi { 1181200590Sluigi u_int32_t tbl, cnt; 1182200590Sluigi 1183200590Sluigi if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), 1184200590Sluigi sizeof(tbl)))) 1185200590Sluigi break; 1186200838Sluigi IPFW_RLOCK(chain); 1187200838Sluigi error = ipfw_count_table(chain, tbl, &cnt); 1188200838Sluigi IPFW_RUNLOCK(chain); 1189200590Sluigi if (error) 1190200590Sluigi break; 1191200590Sluigi error = sooptcopyout(sopt, &cnt, sizeof(cnt)); 1192200590Sluigi } 1193200590Sluigi break; 1194200590Sluigi 1195200590Sluigi case IP_FW_TABLE_LIST: 1196200590Sluigi { 1197200590Sluigi ipfw_table *tbl; 1198200590Sluigi 1199200590Sluigi if (sopt->sopt_valsize < sizeof(*tbl)) { 1200200590Sluigi error = EINVAL; 1201200590Sluigi break; 1202200590Sluigi } 1203200590Sluigi size = sopt->sopt_valsize; 1204200590Sluigi tbl = malloc(size, M_TEMP, M_WAITOK); 1205200590Sluigi error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); 1206200590Sluigi if (error) { 1207200590Sluigi free(tbl, M_TEMP); 1208200590Sluigi break; 1209200590Sluigi } 1210200590Sluigi tbl->size = (size - sizeof(*tbl)) / 1211200590Sluigi sizeof(ipfw_table_entry); 1212200838Sluigi IPFW_RLOCK(chain); 1213200838Sluigi error = ipfw_dump_table(chain, tbl); 1214200838Sluigi IPFW_RUNLOCK(chain); 1215200590Sluigi if (error) { 1216200590Sluigi free(tbl, M_TEMP); 1217200590Sluigi break; 1218200590Sluigi } 1219200590Sluigi error = sooptcopyout(sopt, tbl, size); 1220200590Sluigi free(tbl, M_TEMP); 1221200590Sluigi } 1222200590Sluigi break; 1223200590Sluigi 1224232865Smelifaro case IP_FW_TABLE_XGETSIZE: /* IP_FW3 */ 1225232865Smelifaro { 1226232865Smelifaro uint32_t *tbl; 1227232865Smelifaro 1228232865Smelifaro if (IP_FW3_OPLENGTH(sopt) < sizeof(uint32_t)) { 1229232865Smelifaro error = EINVAL; 1230232865Smelifaro break; 1231232865Smelifaro } 1232232865Smelifaro 1233232865Smelifaro tbl = (uint32_t *)(op3 + 1); 1234232865Smelifaro 1235232865Smelifaro IPFW_RLOCK(chain); 1236232865Smelifaro error = ipfw_count_xtable(chain, *tbl, tbl); 1237232865Smelifaro IPFW_RUNLOCK(chain); 1238232865Smelifaro if (error) 1239232865Smelifaro break; 1240232865Smelifaro error = sooptcopyout(sopt, op3, sopt->sopt_valsize); 1241232865Smelifaro } 1242232865Smelifaro break; 1243232865Smelifaro 1244232865Smelifaro case IP_FW_TABLE_XLIST: /* IP_FW3 */ 1245232865Smelifaro { 1246232865Smelifaro ipfw_xtable *tbl; 1247232865Smelifaro 1248232865Smelifaro if ((size = valsize) < sizeof(ipfw_xtable)) { 1249232865Smelifaro error = EINVAL; 1250232865Smelifaro break; 1251232865Smelifaro } 1252232865Smelifaro 1253232865Smelifaro tbl = malloc(size, M_TEMP, M_ZERO | M_WAITOK); 1254232865Smelifaro memcpy(tbl, op3, sizeof(ipfw_xtable)); 1255232865Smelifaro 1256232865Smelifaro /* Get maximum number of entries we can store */ 1257232865Smelifaro tbl->size = (size - sizeof(ipfw_xtable)) / 1258232865Smelifaro sizeof(ipfw_table_xentry); 1259232865Smelifaro IPFW_RLOCK(chain); 1260232865Smelifaro error = ipfw_dump_xtable(chain, tbl); 1261232865Smelifaro IPFW_RUNLOCK(chain); 1262232865Smelifaro if (error) { 1263232865Smelifaro free(tbl, M_TEMP); 1264232865Smelifaro break; 1265232865Smelifaro } 1266232865Smelifaro 1267232865Smelifaro /* Revert size field back to bytes */ 1268232865Smelifaro tbl->size = tbl->size * sizeof(ipfw_table_xentry) + 1269232865Smelifaro sizeof(ipfw_table); 1270232865Smelifaro /* 1271232865Smelifaro * Since we call sooptcopyin() with small buffer, sopt_valsize is 1272232865Smelifaro * decreased to reflect supplied buffer size. Set it back to original value 1273232865Smelifaro */ 1274232865Smelifaro sopt->sopt_valsize = valsize; 1275232865Smelifaro error = sooptcopyout(sopt, tbl, size); 1276232865Smelifaro free(tbl, M_TEMP); 1277232865Smelifaro } 1278232865Smelifaro break; 1279232865Smelifaro 1280200838Sluigi /*--- NAT operations are protected by the IPFW_LOCK ---*/ 1281200590Sluigi case IP_FW_NAT_CFG: 1282200590Sluigi if (IPFW_NAT_LOADED) 1283200590Sluigi error = ipfw_nat_cfg_ptr(sopt); 1284200590Sluigi else { 1285200590Sluigi printf("IP_FW_NAT_CFG: %s\n", 1286200590Sluigi "ipfw_nat not present, please load it"); 1287200590Sluigi error = EINVAL; 1288200590Sluigi } 1289200590Sluigi break; 1290200590Sluigi 1291200590Sluigi case IP_FW_NAT_DEL: 1292200590Sluigi if (IPFW_NAT_LOADED) 1293200590Sluigi error = ipfw_nat_del_ptr(sopt); 1294200590Sluigi else { 1295200590Sluigi printf("IP_FW_NAT_DEL: %s\n", 1296200590Sluigi "ipfw_nat not present, please load it"); 1297200590Sluigi error = EINVAL; 1298200590Sluigi } 1299200590Sluigi break; 1300200590Sluigi 1301200590Sluigi case IP_FW_NAT_GET_CONFIG: 1302200590Sluigi if (IPFW_NAT_LOADED) 1303200590Sluigi error = ipfw_nat_get_cfg_ptr(sopt); 1304200590Sluigi else { 1305200590Sluigi printf("IP_FW_NAT_GET_CFG: %s\n", 1306200590Sluigi "ipfw_nat not present, please load it"); 1307200590Sluigi error = EINVAL; 1308200590Sluigi } 1309200590Sluigi break; 1310200590Sluigi 1311200590Sluigi case IP_FW_NAT_GET_LOG: 1312200590Sluigi if (IPFW_NAT_LOADED) 1313200590Sluigi error = ipfw_nat_get_log_ptr(sopt); 1314200590Sluigi else { 1315200590Sluigi printf("IP_FW_NAT_GET_LOG: %s\n", 1316200590Sluigi "ipfw_nat not present, please load it"); 1317200590Sluigi error = EINVAL; 1318200590Sluigi } 1319200590Sluigi break; 1320200590Sluigi 1321200590Sluigi default: 1322200590Sluigi printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); 1323200590Sluigi error = EINVAL; 1324200590Sluigi } 1325200590Sluigi 1326200590Sluigi return (error); 1327200590Sluigi#undef RULE_MAXSIZE 1328200590Sluigi} 1329204591Sluigi 1330204591Sluigi 1331204591Sluigi#define RULE_MAXSIZE (256*sizeof(u_int32_t)) 1332204591Sluigi 1333204591Sluigi/* Functions to convert rules 7.2 <==> 8.0 */ 1334204591Sluigiint 1335204591Sluigiconvert_rule_to_7(struct ip_fw *rule) 1336204591Sluigi{ 1337204591Sluigi /* Used to modify original rule */ 1338204591Sluigi struct ip_fw7 *rule7 = (struct ip_fw7 *)rule; 1339204591Sluigi /* copy of original rule, version 8 */ 1340204591Sluigi struct ip_fw *tmp; 1341204591Sluigi 1342204591Sluigi /* Used to copy commands */ 1343204591Sluigi ipfw_insn *ccmd, *dst; 1344204591Sluigi int ll = 0, ccmdlen = 0; 1345204591Sluigi 1346204591Sluigi tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); 1347204591Sluigi if (tmp == NULL) { 1348204591Sluigi return 1; //XXX error 1349204591Sluigi } 1350204591Sluigi bcopy(rule, tmp, RULE_MAXSIZE); 1351204591Sluigi 1352204591Sluigi /* Copy fields */ 1353204591Sluigi rule7->_pad = tmp->_pad; 1354204591Sluigi rule7->set = tmp->set; 1355204591Sluigi rule7->rulenum = tmp->rulenum; 1356204591Sluigi rule7->cmd_len = tmp->cmd_len; 1357204591Sluigi rule7->act_ofs = tmp->act_ofs; 1358204591Sluigi rule7->next_rule = (struct ip_fw7 *)tmp->next_rule; 1359204591Sluigi rule7->next = (struct ip_fw7 *)tmp->x_next; 1360204591Sluigi rule7->cmd_len = tmp->cmd_len; 1361204591Sluigi rule7->pcnt = tmp->pcnt; 1362204591Sluigi rule7->bcnt = tmp->bcnt; 1363204591Sluigi rule7->timestamp = tmp->timestamp; 1364204591Sluigi 1365204591Sluigi /* Copy commands */ 1366204591Sluigi for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ; 1367204591Sluigi ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { 1368204591Sluigi ccmdlen = F_LEN(ccmd); 1369204591Sluigi 1370204591Sluigi bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); 1371204713Sluigi 1372204713Sluigi if (dst->opcode > O_NAT) 1373204713Sluigi /* O_REASS doesn't exists in 7.2 version, so 1374204713Sluigi * decrement opcode if it is after O_REASS 1375204713Sluigi */ 1376204713Sluigi dst->opcode--; 1377204713Sluigi 1378204591Sluigi if (ccmdlen > ll) { 1379204591Sluigi printf("ipfw: opcode %d size truncated\n", 1380204591Sluigi ccmd->opcode); 1381204591Sluigi return EINVAL; 1382204591Sluigi } 1383204591Sluigi } 1384204591Sluigi free(tmp, M_TEMP); 1385204591Sluigi 1386204591Sluigi return 0; 1387204591Sluigi} 1388204591Sluigi 1389204591Sluigiint 1390204591Sluigiconvert_rule_to_8(struct ip_fw *rule) 1391204591Sluigi{ 1392204591Sluigi /* Used to modify original rule */ 1393204591Sluigi struct ip_fw7 *rule7 = (struct ip_fw7 *) rule; 1394204591Sluigi 1395204591Sluigi /* Used to copy commands */ 1396204591Sluigi ipfw_insn *ccmd, *dst; 1397204591Sluigi int ll = 0, ccmdlen = 0; 1398204591Sluigi 1399204591Sluigi /* Copy of original rule */ 1400204591Sluigi struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); 1401204591Sluigi if (tmp == NULL) { 1402204591Sluigi return 1; //XXX error 1403204591Sluigi } 1404204591Sluigi 1405204591Sluigi bcopy(rule7, tmp, RULE_MAXSIZE); 1406204591Sluigi 1407204591Sluigi for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ; 1408204591Sluigi ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { 1409204591Sluigi ccmdlen = F_LEN(ccmd); 1410204591Sluigi 1411204591Sluigi bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); 1412204713Sluigi 1413204713Sluigi if (dst->opcode > O_NAT) 1414204713Sluigi /* O_REASS doesn't exists in 7.2 version, so 1415204713Sluigi * increment opcode if it is after O_REASS 1416204713Sluigi */ 1417204713Sluigi dst->opcode++; 1418204713Sluigi 1419204591Sluigi if (ccmdlen > ll) { 1420204591Sluigi printf("ipfw: opcode %d size truncated\n", 1421204591Sluigi ccmd->opcode); 1422204591Sluigi return EINVAL; 1423204591Sluigi } 1424204591Sluigi } 1425204591Sluigi 1426204591Sluigi rule->_pad = tmp->_pad; 1427204591Sluigi rule->set = tmp->set; 1428204591Sluigi rule->rulenum = tmp->rulenum; 1429204591Sluigi rule->cmd_len = tmp->cmd_len; 1430204591Sluigi rule->act_ofs = tmp->act_ofs; 1431204591Sluigi rule->next_rule = (struct ip_fw *)tmp->next_rule; 1432204591Sluigi rule->x_next = (struct ip_fw *)tmp->next; 1433204591Sluigi rule->cmd_len = tmp->cmd_len; 1434204591Sluigi rule->id = 0; /* XXX see if is ok = 0 */ 1435204591Sluigi rule->pcnt = tmp->pcnt; 1436204591Sluigi rule->bcnt = tmp->bcnt; 1437204591Sluigi rule->timestamp = tmp->timestamp; 1438204591Sluigi 1439204591Sluigi free (tmp, M_TEMP); 1440204591Sluigi return 0; 1441204591Sluigi} 1442204591Sluigi 1443200601Sluigi/* end of file */ 1444