1 /* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Begemot: bsnmp/snmp_mibII/mibII_tcp.c,v 1.7 2005/05/23 09:03:42 brandt_h Exp $ 30 * 31 * tcp 32 */ 33 #include "mibII.h" 34 #include "mibII_oid.h" 35 #include <sys/socketvar.h> 36 #include <netinet/in_pcb.h> 37 #include <netinet/tcp.h> 38 #include <netinet/tcp_var.h> 39 #include <netinet/tcp_timer.h> 40 #include <netinet/tcp_fsm.h> 41 42 struct tcp_index { 43 struct asn_oid index; 44 struct xtcpcb *tp; 45 }; 46 47 static uint64_t tcp_tick; 48 static uint64_t tcp_stats_tick; 49 static struct tcpstat tcpstat; 50 static uint64_t tcps_states[TCP_NSTATES]; 51 static struct xinpgen *xinpgen; 52 static size_t xinpgen_len; 53 static u_int tcp_total; 54 55 static u_int oidnum; 56 static struct tcp_index *tcpoids; 57 58 static int 59 tcp_compare(const void *p1, const void *p2) 60 { 61 const struct tcp_index *t1 = p1; 62 const struct tcp_index *t2 = p2; 63 64 return (asn_compare_oid(&t1->index, &t2->index)); 65 } 66 67 static int 68 fetch_tcp_stats(void) 69 { 70 size_t len; 71 72 len = sizeof(tcpstat); 73 if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, NULL, 0) == -1) { 74 syslog(LOG_ERR, "net.inet.tcp.stats: %m"); 75 return (-1); 76 } 77 if (len != sizeof(tcpstat)) { 78 syslog(LOG_ERR, "net.inet.tcp.stats: wrong size"); 79 return (-1); 80 } 81 82 len = sizeof(tcps_states); 83 if (sysctlbyname("net.inet.tcp.states", &tcps_states, &len, NULL, 84 0) == -1) { 85 syslog(LOG_ERR, "net.inet.tcp.states: %m"); 86 return (-1); 87 } 88 if (len != sizeof(tcps_states)) { 89 syslog(LOG_ERR, "net.inet.tcp.states: wrong size"); 90 return (-1); 91 } 92 93 tcp_stats_tick = get_ticks(); 94 95 return (0); 96 } 97 98 static int 99 fetch_tcp(void) 100 { 101 size_t len; 102 struct xinpgen *ptr; 103 struct xtcpcb *tp; 104 struct tcp_index *oid; 105 in_addr_t inaddr; 106 107 len = 0; 108 if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) == -1) { 109 syslog(LOG_ERR, "net.inet.tcp.pcblist: %m"); 110 return (-1); 111 } 112 if (len > xinpgen_len) { 113 if ((ptr = realloc(xinpgen, len)) == NULL) { 114 syslog(LOG_ERR, "%zu: %m", len); 115 return (-1); 116 } 117 xinpgen = ptr; 118 xinpgen_len = len; 119 } 120 if (sysctlbyname("net.inet.tcp.pcblist", xinpgen, &len, NULL, 0) == -1) { 121 syslog(LOG_ERR, "net.inet.tcp.pcblist: %m"); 122 return (-1); 123 } 124 125 tcp_tick = get_ticks(); 126 127 tcp_total = 0; 128 for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len); 129 ptr->xig_len > sizeof(struct xinpgen); 130 ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) { 131 tp = (struct xtcpcb *)ptr; 132 if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen || 133 (tp->xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) == 0) 134 continue; 135 136 if (tp->xt_inp.inp_vflag & INP_IPV4) 137 tcp_total++; 138 } 139 140 if (oidnum < tcp_total) { 141 oid = realloc(tcpoids, tcp_total * sizeof(tcpoids[0])); 142 if (oid == NULL) { 143 free(tcpoids); 144 oidnum = 0; 145 return (0); 146 } 147 tcpoids = oid; 148 oidnum = tcp_total; 149 } 150 151 oid = tcpoids; 152 for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len); 153 ptr->xig_len > sizeof(struct xinpgen); 154 ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) { 155 tp = (struct xtcpcb *)ptr; 156 if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen || 157 (tp->xt_inp.inp_vflag & INP_IPV4) == 0) 158 continue; 159 oid->tp = tp; 160 oid->index.len = 10; 161 inaddr = ntohl(tp->xt_inp.inp_laddr.s_addr); 162 oid->index.subs[0] = (inaddr >> 24) & 0xff; 163 oid->index.subs[1] = (inaddr >> 16) & 0xff; 164 oid->index.subs[2] = (inaddr >> 8) & 0xff; 165 oid->index.subs[3] = (inaddr >> 0) & 0xff; 166 oid->index.subs[4] = ntohs(tp->xt_inp.inp_lport); 167 inaddr = ntohl(tp->xt_inp.inp_faddr.s_addr); 168 oid->index.subs[5] = (inaddr >> 24) & 0xff; 169 oid->index.subs[6] = (inaddr >> 16) & 0xff; 170 oid->index.subs[7] = (inaddr >> 8) & 0xff; 171 oid->index.subs[8] = (inaddr >> 0) & 0xff; 172 oid->index.subs[9] = ntohs(tp->xt_inp.inp_fport); 173 oid++; 174 } 175 176 qsort(tcpoids, tcp_total, sizeof(tcpoids[0]), tcp_compare); 177 178 return (0); 179 } 180 181 /* 182 * Scalars 183 */ 184 int 185 op_tcp(struct snmp_context *ctx __unused, struct snmp_value *value, 186 u_int sub, u_int iidx __unused, enum snmp_op op) 187 { 188 switch (op) { 189 190 case SNMP_OP_GETNEXT: 191 abort(); 192 193 case SNMP_OP_GET: 194 break; 195 196 case SNMP_OP_SET: 197 return (SNMP_ERR_NOT_WRITEABLE); 198 199 case SNMP_OP_ROLLBACK: 200 case SNMP_OP_COMMIT: 201 abort(); 202 } 203 204 if (tcp_stats_tick < this_tick) 205 if (fetch_tcp_stats() == -1) 206 return (SNMP_ERR_GENERR); 207 208 switch (value->var.subs[sub - 1]) { 209 210 case LEAF_tcpRtoAlgorithm: 211 value->v.integer = 4; /* Van Jacobson */ 212 break; 213 214 #define hz clockinfo.hz 215 216 case LEAF_tcpRtoMin: 217 value->v.integer = 1000 * TCPTV_MIN / hz; 218 break; 219 220 case LEAF_tcpRtoMax: 221 value->v.integer = 1000 * TCPTV_REXMTMAX / hz; 222 break; 223 #undef hz 224 225 case LEAF_tcpMaxConn: 226 value->v.integer = -1; 227 break; 228 229 case LEAF_tcpActiveOpens: 230 value->v.uint32 = tcpstat.tcps_connattempt; 231 break; 232 233 case LEAF_tcpPassiveOpens: 234 value->v.uint32 = tcpstat.tcps_accepts; 235 break; 236 237 case LEAF_tcpAttemptFails: 238 value->v.uint32 = tcpstat.tcps_conndrops; 239 break; 240 241 case LEAF_tcpEstabResets: 242 value->v.uint32 = tcpstat.tcps_drops; 243 break; 244 245 case LEAF_tcpCurrEstab: 246 value->v.uint32 = tcps_states[TCPS_ESTABLISHED] + 247 tcps_states[TCPS_CLOSE_WAIT]; 248 break; 249 250 case LEAF_tcpInSegs: 251 value->v.uint32 = tcpstat.tcps_rcvtotal; 252 break; 253 254 case LEAF_tcpOutSegs: 255 value->v.uint32 = tcpstat.tcps_sndtotal - 256 tcpstat.tcps_sndrexmitpack; 257 break; 258 259 case LEAF_tcpRetransSegs: 260 value->v.uint32 = tcpstat.tcps_sndrexmitpack; 261 break; 262 263 case LEAF_tcpInErrs: 264 value->v.uint32 = tcpstat.tcps_rcvbadsum + 265 tcpstat.tcps_rcvbadoff + 266 tcpstat.tcps_rcvshort; 267 break; 268 } 269 return (SNMP_ERR_NOERROR); 270 } 271 272 int 273 op_tcpconn(struct snmp_context *ctx __unused, struct snmp_value *value, 274 u_int sub, u_int iidx __unused, enum snmp_op op) 275 { 276 u_int i; 277 278 if (tcp_tick < this_tick) 279 if (fetch_tcp() == -1) 280 return (SNMP_ERR_GENERR); 281 282 switch (op) { 283 284 case SNMP_OP_GETNEXT: 285 for (i = 0; i < tcp_total; i++) 286 if (index_compare(&value->var, sub, &tcpoids[i].index) < 0) 287 break; 288 if (i == tcp_total) 289 return (SNMP_ERR_NOSUCHNAME); 290 index_append(&value->var, sub, &tcpoids[i].index); 291 break; 292 293 case SNMP_OP_GET: 294 for (i = 0; i < tcp_total; i++) 295 if (index_compare(&value->var, sub, &tcpoids[i].index) == 0) 296 break; 297 if (i == tcp_total) 298 return (SNMP_ERR_NOSUCHNAME); 299 break; 300 301 case SNMP_OP_SET: 302 return (SNMP_ERR_NOT_WRITEABLE); 303 304 case SNMP_OP_ROLLBACK: 305 case SNMP_OP_COMMIT: 306 default: 307 abort(); 308 } 309 310 switch (value->var.subs[sub - 1]) { 311 312 case LEAF_tcpConnState: 313 switch (tcpoids[i].tp->xt_tp.t_state) { 314 315 case TCPS_CLOSED: 316 value->v.integer = 1; 317 break; 318 case TCPS_LISTEN: 319 value->v.integer = 2; 320 break; 321 case TCPS_SYN_SENT: 322 value->v.integer = 3; 323 break; 324 case TCPS_SYN_RECEIVED: 325 value->v.integer = 4; 326 break; 327 case TCPS_ESTABLISHED: 328 value->v.integer = 5; 329 break; 330 case TCPS_CLOSE_WAIT: 331 value->v.integer = 8; 332 break; 333 case TCPS_FIN_WAIT_1: 334 value->v.integer = 6; 335 break; 336 case TCPS_CLOSING: 337 value->v.integer = 10; 338 break; 339 case TCPS_LAST_ACK: 340 value->v.integer = 9; 341 break; 342 case TCPS_FIN_WAIT_2: 343 value->v.integer = 7; 344 break; 345 case TCPS_TIME_WAIT: 346 value->v.integer = 11; 347 break; 348 default: 349 value->v.integer = 0; 350 break; 351 } 352 break; 353 354 case LEAF_tcpConnLocalAddress: 355 value->v.ipaddress[0] = tcpoids[i].index.subs[0]; 356 value->v.ipaddress[1] = tcpoids[i].index.subs[1]; 357 value->v.ipaddress[2] = tcpoids[i].index.subs[2]; 358 value->v.ipaddress[3] = tcpoids[i].index.subs[3]; 359 break; 360 361 case LEAF_tcpConnLocalPort: 362 value->v.integer = tcpoids[i].index.subs[4]; 363 break; 364 365 case LEAF_tcpConnRemAddress: 366 value->v.ipaddress[0] = tcpoids[i].index.subs[5]; 367 value->v.ipaddress[1] = tcpoids[i].index.subs[6]; 368 value->v.ipaddress[2] = tcpoids[i].index.subs[7]; 369 value->v.ipaddress[3] = tcpoids[i].index.subs[8]; 370 break; 371 372 case LEAF_tcpConnRemPort: 373 value->v.integer = tcpoids[i].index.subs[9]; 374 break; 375 } 376 return (SNMP_ERR_NOERROR); 377 } 378