1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
14 */
15
16 /*
17 * Replacement for __xnet_sendmsg() which will perform rate-limiting based
18 * on various factors.
19 *
20 * This should only be LD_PRELOAD-ed against in.iked, which is closed source,
21 * but we do know a few things about it:
22 *
23 * 1.) It's single-threaded, apart from the IKE door thread. The door thread
24 * is kept in check by a mutex deep inside libike, which is held during
25 * most of its runtime, including calls to sendmsg().
26 *
27 * 2.) By default, it has one socket for every local address, bound to
28 * UDP port 500, and one for every IPv4 local addres, bound to UDP port 4500
29 * (NAT-Traversal).
30 *
31 * We can exploit what we know to make simplifying assumptions, keep global
32 * state, and other fun things.
33 */
34
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <sys/socket.h>
38 #include <sys/stropts.h>
39 #include <sys/stream.h>
40 #include <sys/socketvar.h>
41 #include <sys/sockio.h>
42
43 #include <errno.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <stropts.h>
47 #include <stdio.h>
48 #include <strings.h>
49
50 /* For the actual syscall... */
51 extern int _so_sendmsg();
52
53 /* For now, settable with mdb -p `pgrep in.iked` */
54 /* XXX KEBE ASKS, should we sugar-coat that with "ikeadm ratelimit {on,off}"? */
55 boolean_t RL_ike_sendmsg;
56
57 hrtime_t last_packet_sent = 0;
58 hrtime_t rapid_fire_threshold = MSEC2NSEC(500); /* 1/2 second */
59 int rapid_fire_count = 8;
60 int current_spray = 0;
61
62 /*
63 * Return B_TRUE if we think we can send the packet.
64 *
65 * For starters, base it on more than "rapid_fire_count" packets
66 * taking less than "rapid_fire_threshhold" each. No DPI.
67 */
68 /* ARGSUSED */
69 static boolean_t
70 evaluate_situation(int sock, const struct msghdr *msg)
71 {
72 hrtime_t now;
73 struct sockaddr_in6 sin6;
74 socklen_t socklen = sizeof (sin6);
75
76 if (getsockname(sock, (struct sockaddr *)&sin6, &socklen) == -1) {
77 /* HUH wha?!? */
78 /* XXX KEBE SAYS PUT A DTRACE PROBE IN HERE! */
79 return (B_TRUE);
80 }
81 /* sin6_port & sin_port have the same offset. */
82 if (htons(sin6.sin6_port) != 500 && htons(sin6.sin6_port) != 4500) {
83 /* Must be doing DNS or LDAP... not an IKE packet. */
84 /* XXX KEBE SAYS PUT A DTRACE PROBE IN HERE! */
85 return (B_TRUE);
86 }
87
88 now = gethrtime();
89 if (last_packet_sent + rapid_fire_threshold < now) {
90 if (current_spray > 0)
91 current_spray--;
92 goto good;
93 }
94
95 /* XXX KEBE ASKS DTRACE PROBE FOR RAPID FIRE INCREASE? */
96 current_spray++;
97 if (current_spray < rapid_fire_count)
98 goto good;
99
100 /* We should never have the current spray exceed rapid_fire_count. */
101 current_spray = rapid_fire_count;
102
103 /* XXX KEBE SAYS PUT A DTRACE PROBE IN HERE! */
104
105 /* Drop the packet. */
106 return (B_FALSE);
107
108 good:
109 last_packet_sent = now;
110 return (B_TRUE);
111 }
112
113 int
114 __xnet_sendmsg(int sock, const struct msghdr *msg, int flags)
115 {
116 /* Fast path... */
117 if (!RL_ike_sendmsg || evaluate_situation(sock, msg))
118 return (_so_sendmsg(sock, msg, flags | MSG_XPG4_2));
119 else {
120 /*
121 * Don't actually send the message, but pretend like you did.
122 *
123 * Maybe we could return -1 and set errno to something, but
124 * knowing what's right would require further disassembling of
125 * libike, or having libike source (which is closed).
126 */
127 return (0);
128 }
129 }
130