ip6_divert.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /* $OpenBSD: ip6_divert.c,v 1.36 2015/07/15 22:16:42 deraadt Exp $ */
  2. /*
  3. * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/param.h>
  18. #include <sys/systm.h>
  19. #include <sys/mbuf.h>
  20. #include <sys/protosw.h>
  21. #include <sys/socket.h>
  22. #include <sys/socketvar.h>
  23. #include <sys/sysctl.h>
  24. #include <net/if.h>
  25. #include <net/route.h>
  26. #include <net/if_var.h>
  27. #include <net/netisr.h>
  28. #include <netinet/in.h>
  29. #include <netinet/ip.h>
  30. #include <netinet/ip_var.h>
  31. #include <netinet/in_pcb.h>
  32. #include <netinet/ip6.h>
  33. #include <netinet6/in6_var.h>
  34. #include <netinet6/ip6_divert.h>
  35. #include <netinet/tcp.h>
  36. #include <netinet/udp.h>
  37. #include <netinet/icmp6.h>
  38. #include <net/pfvar.h>
  39. struct inpcbtable divb6table;
  40. struct div6stat div6stat;
  41. #ifndef DIVERT_SENDSPACE
  42. #define DIVERT_SENDSPACE (65536 + 100)
  43. #endif
  44. u_int divert6_sendspace = DIVERT_SENDSPACE;
  45. #ifndef DIVERT_RECVSPACE
  46. #define DIVERT_RECVSPACE (65536 + 100)
  47. #endif
  48. u_int divert6_recvspace = DIVERT_RECVSPACE;
  49. #ifndef DIVERTHASHSIZE
  50. #define DIVERTHASHSIZE 128
  51. #endif
  52. int *divert6ctl_vars[DIVERT6CTL_MAXID] = DIVERT6CTL_VARS;
  53. int divb6hashsize = DIVERTHASHSIZE;
  54. static struct sockaddr_in6 ip6addr = { sizeof(ip6addr), AF_INET6 };
  55. void divert6_detach(struct inpcb *);
  56. int divert6_output(struct inpcb *, struct mbuf *, struct mbuf *,
  57. struct mbuf *);
  58. void
  59. divert6_init()
  60. {
  61. in_pcbinit(&divb6table, divb6hashsize);
  62. }
  63. int
  64. divert6_input(struct mbuf **mp, int *offp, int proto)
  65. {
  66. m_freem(*mp);
  67. return (0);
  68. }
  69. int
  70. divert6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
  71. struct mbuf *control)
  72. {
  73. struct sockaddr_in6 *sin6;
  74. struct socket *so;
  75. struct ifaddr *ifa;
  76. int error = 0, min_hdrlen = 0, nxt = 0, off, dir;
  77. struct ip6_hdr *ip6;
  78. m->m_pkthdr.ph_ifidx = 0;
  79. m->m_nextpkt = NULL;
  80. m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
  81. m_freem(control);
  82. sin6 = mtod(nam, struct sockaddr_in6 *);
  83. so = inp->inp_socket;
  84. /* Do basic sanity checks. */
  85. if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
  86. goto fail;
  87. if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
  88. /* m_pullup() has freed the mbuf, so just return. */
  89. div6stat.divs_errors++;
  90. return (ENOBUFS);
  91. }
  92. ip6 = mtod(m, struct ip6_hdr *);
  93. if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
  94. goto fail;
  95. if (m->m_pkthdr.len < sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen))
  96. goto fail;
  97. /*
  98. * Recalculate the protocol checksum since the userspace application
  99. * may have modified the packet prior to reinjection.
  100. */
  101. off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
  102. if (off < sizeof(struct ip6_hdr))
  103. goto fail;
  104. dir = (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ? PF_OUT : PF_IN);
  105. switch (nxt) {
  106. case IPPROTO_TCP:
  107. min_hdrlen = sizeof(struct tcphdr);
  108. m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
  109. break;
  110. case IPPROTO_UDP:
  111. min_hdrlen = sizeof(struct udphdr);
  112. m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
  113. break;
  114. case IPPROTO_ICMPV6:
  115. min_hdrlen = sizeof(struct icmp6_hdr);
  116. m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
  117. break;
  118. default:
  119. /* nothing */
  120. break;
  121. }
  122. if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen)
  123. goto fail;
  124. m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET;
  125. if (dir == PF_IN) {
  126. ip6addr.sin6_addr = sin6->sin6_addr;
  127. ifa = ifa_ifwithaddr(sin6tosa(&ip6addr),
  128. m->m_pkthdr.ph_rtableid);
  129. if (ifa == NULL) {
  130. error = EADDRNOTAVAIL;
  131. goto fail;
  132. }
  133. m->m_pkthdr.ph_ifidx = ifa->ifa_ifp->if_index;
  134. /*
  135. * Recalculate the protocol checksum for the inbound packet
  136. * since the userspace application may have modified the packet
  137. * prior to reinjection.
  138. */
  139. in6_proto_cksum_out(m, NULL);
  140. niq_enqueue(&ip6intrq, m); /* return error on q full? */
  141. } else {
  142. error = ip6_output(m, NULL, &inp->inp_route6,
  143. IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL, NULL);
  144. }
  145. div6stat.divs_opackets++;
  146. return (error);
  147. fail:
  148. div6stat.divs_errors++;
  149. m_freem(m);
  150. return (error ? error : EINVAL);
  151. }
  152. int
  153. divert6_packet(struct mbuf *m, int dir, u_int16_t divert_port)
  154. {
  155. struct inpcb *inp;
  156. struct socket *sa = NULL;
  157. struct sockaddr_in6 addr;
  158. inp = NULL;
  159. div6stat.divs_ipackets++;
  160. if (m->m_len < sizeof(struct ip6_hdr) &&
  161. (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
  162. div6stat.divs_errors++;
  163. return (0);
  164. }
  165. TAILQ_FOREACH(inp, &divb6table.inpt_queue, inp_queue) {
  166. if (inp->inp_lport != divert_port)
  167. continue;
  168. if (inp->inp_divertfl == 0)
  169. break;
  170. if (dir == PF_IN && !(inp->inp_divertfl & IPPROTO_DIVERT_RESP))
  171. return (-1);
  172. if (dir == PF_OUT && !(inp->inp_divertfl & IPPROTO_DIVERT_INIT))
  173. return (-1);
  174. break;
  175. }
  176. memset(&addr, 0, sizeof(addr));
  177. addr.sin6_family = AF_INET6;
  178. addr.sin6_len = sizeof(addr);
  179. if (dir == PF_IN) {
  180. struct ifaddr *ifa;
  181. struct ifnet *ifp;
  182. ifp = if_get(m->m_pkthdr.ph_ifidx);
  183. if (ifp == NULL) {
  184. m_freem(m);
  185. return (0);
  186. }
  187. TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
  188. if (ifa->ifa_addr->sa_family != AF_INET6)
  189. continue;
  190. addr.sin6_addr = satosin6(ifa->ifa_addr)->sin6_addr;
  191. break;
  192. }
  193. }
  194. if (inp) {
  195. sa = inp->inp_socket;
  196. if (sbappendaddr(&sa->so_rcv, sin6tosa(&addr), m, NULL) == 0) {
  197. div6stat.divs_fullsock++;
  198. m_freem(m);
  199. return (0);
  200. } else
  201. sorwakeup(inp->inp_socket);
  202. }
  203. if (sa == NULL) {
  204. div6stat.divs_noport++;
  205. m_freem(m);
  206. }
  207. return (0);
  208. }
  209. /*ARGSUSED*/
  210. int
  211. divert6_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *addr,
  212. struct mbuf *control, struct proc *p)
  213. {
  214. struct inpcb *inp = sotoinpcb(so);
  215. int error = 0;
  216. int s;
  217. if (req == PRU_CONTROL) {
  218. return (in6_control(so, (u_long)m, (caddr_t)addr,
  219. (struct ifnet *)control));
  220. }
  221. if (inp == NULL && req != PRU_ATTACH) {
  222. error = EINVAL;
  223. goto release;
  224. }
  225. switch (req) {
  226. case PRU_ATTACH:
  227. if (inp != NULL) {
  228. error = EINVAL;
  229. break;
  230. }
  231. if ((so->so_state & SS_PRIV) == 0) {
  232. error = EACCES;
  233. break;
  234. }
  235. s = splsoftnet();
  236. error = in_pcballoc(so, &divb6table);
  237. splx(s);
  238. if (error)
  239. break;
  240. error = soreserve(so, divert6_sendspace, divert6_recvspace);
  241. if (error)
  242. break;
  243. sotoinpcb(so)->inp_flags |= INP_HDRINCL;
  244. break;
  245. case PRU_DETACH:
  246. divert6_detach(inp);
  247. break;
  248. case PRU_BIND:
  249. s = splsoftnet();
  250. error = in6_pcbbind(inp, addr, p);
  251. splx(s);
  252. break;
  253. case PRU_SHUTDOWN:
  254. socantsendmore(so);
  255. break;
  256. case PRU_SEND:
  257. return (divert6_output(inp, m, addr, control));
  258. case PRU_ABORT:
  259. soisdisconnected(so);
  260. divert6_detach(inp);
  261. break;
  262. case PRU_SOCKADDR:
  263. in6_setsockaddr(inp, addr);
  264. break;
  265. case PRU_PEERADDR:
  266. in6_setpeeraddr(inp, addr);
  267. break;
  268. case PRU_SENSE:
  269. return (0);
  270. case PRU_LISTEN:
  271. case PRU_CONNECT:
  272. case PRU_CONNECT2:
  273. case PRU_ACCEPT:
  274. case PRU_DISCONNECT:
  275. case PRU_SENDOOB:
  276. case PRU_FASTTIMO:
  277. case PRU_SLOWTIMO:
  278. case PRU_PROTORCV:
  279. case PRU_PROTOSEND:
  280. error = EOPNOTSUPP;
  281. break;
  282. case PRU_RCVD:
  283. case PRU_RCVOOB:
  284. return (EOPNOTSUPP); /* do not free mbuf's */
  285. default:
  286. panic("divert6_usrreq");
  287. }
  288. release:
  289. m_freem(control);
  290. m_freem(m);
  291. return (error);
  292. }
  293. void
  294. divert6_detach(struct inpcb *inp)
  295. {
  296. int s = splsoftnet();
  297. in_pcbdetach(inp);
  298. splx(s);
  299. }
  300. /*
  301. * Sysctl for divert variables.
  302. */
  303. int
  304. divert6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
  305. void *newp, size_t newlen)
  306. {
  307. /* All sysctl names at this level are terminal. */
  308. if (namelen != 1)
  309. return (ENOTDIR);
  310. switch (name[0]) {
  311. case DIVERT6CTL_SENDSPACE:
  312. return (sysctl_int(oldp, oldlenp, newp, newlen,
  313. &divert6_sendspace));
  314. case DIVERT6CTL_RECVSPACE:
  315. return (sysctl_int(oldp, oldlenp, newp, newlen,
  316. &divert6_recvspace));
  317. case DIVERT6CTL_STATS:
  318. if (newp != NULL)
  319. return (EPERM);
  320. return (sysctl_struct(oldp, oldlenp, newp, newlen,
  321. &div6stat, sizeof(div6stat)));
  322. default:
  323. if (name[0] < DIVERT6CTL_MAXID)
  324. return sysctl_int_arr(divert6ctl_vars, name, namelen,
  325. oldp, oldlenp, newp, newlen);
  326. return (ENOPROTOOPT);
  327. }
  328. /* NOTREACHED */
  329. }