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