pf_ruleset.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /* $OpenBSD: pf_ruleset.c,v 1.10 2015/07/18 19:19:00 sashan Exp $ */
  2. /*
  3. * Copyright (c) 2001 Daniel Hartmeier
  4. * Copyright (c) 2002,2003 Henning Brauer
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * - Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * - Redistributions in binary form must reproduce the above
  14. * copyright notice, this list of conditions and the following
  15. * disclaimer in the documentation and/or other materials provided
  16. * with the distribution.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  21. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  22. * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  23. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  24. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  26. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  27. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  28. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29. * POSSIBILITY OF SUCH DAMAGE.
  30. *
  31. * Effort sponsored in part by the Defense Advanced Research Projects
  32. * Agency (DARPA) and Air Force Research Laboratory, Air Force
  33. * Materiel Command, USAF, under agreement number F30602-01-2-0537.
  34. *
  35. */
  36. #include <sys/param.h>
  37. #include <sys/socket.h>
  38. #ifdef _KERNEL
  39. # include <sys/systm.h>
  40. # include <sys/mbuf.h>
  41. #endif /* _KERNEL */
  42. #include <sys/syslog.h>
  43. #include <netinet/in.h>
  44. #include <netinet/ip.h>
  45. #include <netinet/tcp.h>
  46. #include <net/if.h>
  47. #include <net/pfvar.h>
  48. #ifdef INET6
  49. #include <netinet/ip6.h>
  50. #endif /* INET6 */
  51. #ifdef _KERNEL
  52. #define rs_malloc(x) malloc(x, M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO)
  53. #define rs_free(x) free(x, M_TEMP, 0)
  54. #else /* !_KERNEL */
  55. /* Userland equivalents so we can lend code to pfctl et al. */
  56. # include <arpa/inet.h>
  57. # include <errno.h>
  58. # include <stdio.h>
  59. # include <stdlib.h>
  60. # include <string.h>
  61. # define rs_malloc(x) calloc(1, x)
  62. # define rs_free(x) free(x)
  63. # ifdef PFDEBUG
  64. # include <sys/stdarg.h> /* for DPFPRINTF() */
  65. # endif /* PFDEBUG */
  66. #endif /* _KERNEL */
  67. struct pf_anchor_global pf_anchors;
  68. struct pf_anchor pf_main_anchor;
  69. static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
  70. RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
  71. RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
  72. static __inline int
  73. pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
  74. {
  75. int c = strcmp(a->path, b->path);
  76. return (c ? (c < 0 ? -1 : 1) : 0);
  77. }
  78. void
  79. pf_init_ruleset(struct pf_ruleset *ruleset)
  80. {
  81. memset(ruleset, 0, sizeof(struct pf_ruleset));
  82. TAILQ_INIT(&ruleset->rules.queues[0]);
  83. TAILQ_INIT(&ruleset->rules.queues[1]);
  84. ruleset->rules.active.ptr = &ruleset->rules.queues[0];
  85. ruleset->rules.inactive.ptr = &ruleset->rules.queues[1];
  86. }
  87. struct pf_anchor *
  88. pf_find_anchor(const char *path)
  89. {
  90. struct pf_anchor *key, *found;
  91. key = (struct pf_anchor *)rs_malloc(sizeof(*key));
  92. if (key == NULL)
  93. return (NULL);
  94. strlcpy(key->path, path, sizeof(key->path));
  95. found = RB_FIND(pf_anchor_global, &pf_anchors, key);
  96. rs_free(key);
  97. return (found);
  98. }
  99. struct pf_ruleset *
  100. pf_find_ruleset(const char *path)
  101. {
  102. struct pf_anchor *anchor;
  103. while (*path == '/')
  104. path++;
  105. if (!*path)
  106. return (&pf_main_ruleset);
  107. anchor = pf_find_anchor(path);
  108. if (anchor == NULL)
  109. return (NULL);
  110. else
  111. return (&anchor->ruleset);
  112. }
  113. struct pf_ruleset *
  114. pf_find_or_create_ruleset(const char *path)
  115. {
  116. char *p, *q, *r;
  117. struct pf_ruleset *ruleset;
  118. struct pf_anchor *anchor, *dup, *parent = NULL;
  119. if (path[0] == 0)
  120. return (&pf_main_ruleset);
  121. while (*path == '/')
  122. path++;
  123. ruleset = pf_find_ruleset(path);
  124. if (ruleset != NULL)
  125. return (ruleset);
  126. p = (char *)rs_malloc(MAXPATHLEN);
  127. if (p == NULL)
  128. return (NULL);
  129. strlcpy(p, path, MAXPATHLEN);
  130. while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
  131. *q = 0;
  132. if ((ruleset = pf_find_ruleset(p)) != NULL) {
  133. parent = ruleset->anchor;
  134. break;
  135. }
  136. }
  137. if (q == NULL)
  138. q = p;
  139. else
  140. q++;
  141. strlcpy(p, path, MAXPATHLEN);
  142. if (!*q) {
  143. rs_free(p);
  144. return (NULL);
  145. }
  146. while ((r = strchr(q, '/')) != NULL || *q) {
  147. if (r != NULL)
  148. *r = 0;
  149. if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
  150. (parent != NULL && strlen(parent->path) >=
  151. MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
  152. rs_free(p);
  153. return (NULL);
  154. }
  155. anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor));
  156. if (anchor == NULL) {
  157. rs_free(p);
  158. return (NULL);
  159. }
  160. RB_INIT(&anchor->children);
  161. strlcpy(anchor->name, q, sizeof(anchor->name));
  162. if (parent != NULL) {
  163. strlcpy(anchor->path, parent->path,
  164. sizeof(anchor->path));
  165. strlcat(anchor->path, "/", sizeof(anchor->path));
  166. }
  167. strlcat(anchor->path, anchor->name, sizeof(anchor->path));
  168. if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) !=
  169. NULL) {
  170. DPFPRINTF(LOG_NOTICE,
  171. "pf_find_or_create_ruleset: RB_INSERT1 "
  172. "'%s' '%s' collides with '%s' '%s'",
  173. anchor->path, anchor->name, dup->path, dup->name);
  174. rs_free(anchor);
  175. rs_free(p);
  176. return (NULL);
  177. }
  178. if (parent != NULL) {
  179. anchor->parent = parent;
  180. if ((dup = RB_INSERT(pf_anchor_node, &parent->children,
  181. anchor)) != NULL) {
  182. DPFPRINTF(LOG_NOTICE,
  183. "pf_find_or_create_ruleset: "
  184. "RB_INSERT2 '%s' '%s' collides with "
  185. "'%s' '%s'", anchor->path, anchor->name,
  186. dup->path, dup->name);
  187. RB_REMOVE(pf_anchor_global, &pf_anchors,
  188. anchor);
  189. rs_free(anchor);
  190. rs_free(p);
  191. return (NULL);
  192. }
  193. }
  194. pf_init_ruleset(&anchor->ruleset);
  195. anchor->ruleset.anchor = anchor;
  196. parent = anchor;
  197. if (r != NULL)
  198. q = r + 1;
  199. else
  200. *q = 0;
  201. }
  202. rs_free(p);
  203. return (&anchor->ruleset);
  204. }
  205. void
  206. pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
  207. {
  208. struct pf_anchor *parent;
  209. while (ruleset != NULL) {
  210. if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
  211. !RB_EMPTY(&ruleset->anchor->children) ||
  212. ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
  213. ruleset->topen)
  214. return;
  215. if (!TAILQ_EMPTY(ruleset->rules.active.ptr) ||
  216. !TAILQ_EMPTY(ruleset->rules.inactive.ptr) ||
  217. ruleset->rules.inactive.open)
  218. return;
  219. RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor);
  220. if ((parent = ruleset->anchor->parent) != NULL)
  221. RB_REMOVE(pf_anchor_node, &parent->children,
  222. ruleset->anchor);
  223. rs_free(ruleset->anchor);
  224. if (parent == NULL)
  225. return;
  226. ruleset = &parent->ruleset;
  227. }
  228. }
  229. int
  230. pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
  231. const char *name)
  232. {
  233. char *p, *path;
  234. struct pf_ruleset *ruleset;
  235. r->anchor = NULL;
  236. r->anchor_relative = 0;
  237. r->anchor_wildcard = 0;
  238. if (!name[0])
  239. return (0);
  240. path = (char *)rs_malloc(MAXPATHLEN);
  241. if (path == NULL)
  242. return (1);
  243. if (name[0] == '/')
  244. strlcpy(path, name + 1, MAXPATHLEN);
  245. else {
  246. /* relative path */
  247. r->anchor_relative = 1;
  248. if (s->anchor == NULL || !s->anchor->path[0])
  249. path[0] = 0;
  250. else
  251. strlcpy(path, s->anchor->path, MAXPATHLEN);
  252. while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
  253. if (!path[0]) {
  254. DPFPRINTF(LOG_NOTICE,
  255. "pf_anchor_setup: .. beyond root");
  256. rs_free(path);
  257. return (1);
  258. }
  259. if ((p = strrchr(path, '/')) != NULL)
  260. *p = 0;
  261. else
  262. path[0] = 0;
  263. r->anchor_relative++;
  264. name += 3;
  265. }
  266. if (path[0])
  267. strlcat(path, "/", MAXPATHLEN);
  268. strlcat(path, name, MAXPATHLEN);
  269. }
  270. if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
  271. r->anchor_wildcard = 1;
  272. *p = 0;
  273. }
  274. ruleset = pf_find_or_create_ruleset(path);
  275. rs_free(path);
  276. if (ruleset == NULL || ruleset->anchor == NULL) {
  277. DPFPRINTF(LOG_NOTICE,
  278. "pf_anchor_setup: ruleset");
  279. return (1);
  280. }
  281. r->anchor = ruleset->anchor;
  282. r->anchor->refcnt++;
  283. return (0);
  284. }
  285. int
  286. pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
  287. struct pfioc_rule *pr)
  288. {
  289. pr->anchor_call[0] = 0;
  290. if (r->anchor == NULL)
  291. return (0);
  292. if (!r->anchor_relative) {
  293. strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
  294. strlcat(pr->anchor_call, r->anchor->path,
  295. sizeof(pr->anchor_call));
  296. } else {
  297. char *a, *p;
  298. int i;
  299. a = (char *)rs_malloc(MAXPATHLEN);
  300. if (a == NULL)
  301. return (1);
  302. if (rs->anchor == NULL)
  303. a[0] = 0;
  304. else
  305. strlcpy(a, rs->anchor->path, MAXPATHLEN);
  306. for (i = 1; i < r->anchor_relative; ++i) {
  307. if ((p = strrchr(a, '/')) == NULL)
  308. p = a;
  309. *p = 0;
  310. strlcat(pr->anchor_call, "../",
  311. sizeof(pr->anchor_call));
  312. }
  313. if (strncmp(a, r->anchor->path, strlen(a))) {
  314. DPFPRINTF(LOG_NOTICE,
  315. "pf_anchor_copyout: '%s' '%s'", a,
  316. r->anchor->path);
  317. rs_free(a);
  318. return (1);
  319. }
  320. if (strlen(r->anchor->path) > strlen(a))
  321. strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
  322. strlen(a) + 1 : 0), sizeof(pr->anchor_call));
  323. rs_free(a);
  324. }
  325. if (r->anchor_wildcard)
  326. strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
  327. sizeof(pr->anchor_call));
  328. return (0);
  329. }
  330. void
  331. pf_anchor_remove(struct pf_rule *r)
  332. {
  333. if (r->anchor == NULL)
  334. return;
  335. if (r->anchor->refcnt <= 0) {
  336. DPFPRINTF(LOG_NOTICE,
  337. "pf_anchor_remove: broken refcount");
  338. r->anchor = NULL;
  339. return;
  340. }
  341. if (!--r->anchor->refcnt)
  342. pf_remove_if_empty_ruleset(&r->anchor->ruleset);
  343. r->anchor = NULL;
  344. }