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_fsm.h> 40 41 struct tcp_index { 42 struct asn_oid index; 43 struct xtcpcb *tp; 44 }; 45 46 static uint64_t tcp_tick; 47 static uint64_t tcp_stats_tick; 48 static struct tcpstat tcpstat; 49 static uint64_t tcps_states[TCP_NSTATES]; 50 static struct xinpgen *xinpgen; 51 static size_t xinpgen_len; 52 static u_int tcp_total; 53 static int tcp_rexmit_min; 54 static int tcp_rexmit_max; 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 len = sizeof(tcp_rexmit_min); 94 if (sysctlbyname("net.inet.tcp.rexmit_min", &tcp_rexmit_min, &len, 95 NULL, 0) == -1) { 96 syslog(LOG_ERR, "net.inet.tcp.rexmit_min: %m"); 97 return (-1); 98 } 99 if (len != sizeof(tcp_rexmit_min)) { 100 syslog(LOG_ERR, "net.inet.tcp.rexmit_min: wrong size"); 101 return (-1); 102 } 103 104 len = sizeof(tcp_rexmit_max); 105 if (sysctlbyname("net.inet.tcp.rexmit_max", &tcp_rexmit_max, &len, 106 NULL, 0) == -1) { 107 syslog(LOG_ERR, "net.inet.tcp.rexmit_max: %m"); 108 return (-1); 109 } 110 if (len != sizeof(tcp_rexmit_max)) { 111 syslog(LOG_ERR, "net.inet.tcp.rexmit_max: wrong size"); 112 return (-1); 113 } 114 115 tcp_stats_tick = get_ticks(); 116 117 return (0); 118 } 119 120 static int 121 fetch_tcp(void) 122 { 123 size_t len; 124 struct xinpgen *ptr; 125 struct xtcpcb *tp; 126 struct tcp_index *oid; 127 in_addr_t inaddr; 128 129 len = 0; 130 if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) == -1) { 131 syslog(LOG_ERR, "net.inet.tcp.pcblist: %m"); 132 return (-1); 133 } 134 if (len > xinpgen_len) { 135 if ((ptr = realloc(xinpgen, len)) == NULL) { 136 syslog(LOG_ERR, "%zu: %m", len); 137 return (-1); 138 } 139 xinpgen = ptr; 140 xinpgen_len = len; 141 } 142 if (sysctlbyname("net.inet.tcp.pcblist", xinpgen, &len, NULL, 0) == -1) { 143 syslog(LOG_ERR, "net.inet.tcp.pcblist: %m"); 144 return (-1); 145 } 146 147 tcp_tick = get_ticks(); 148 149 tcp_total = 0; 150 for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len); 151 ptr->xig_len > sizeof(struct xinpgen); 152 ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) { 153 tp = (struct xtcpcb *)ptr; 154 if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen || 155 (tp->xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) == 0) 156 continue; 157 158 if (tp->xt_inp.inp_vflag & INP_IPV4) 159 tcp_total++; 160 } 161 162 if (oidnum < tcp_total) { 163 oid = realloc(tcpoids, tcp_total * sizeof(tcpoids[0])); 164 if (oid == NULL) { 165 free(tcpoids); 166 oidnum = 0; 167 return (0); 168 } 169 tcpoids = oid; 170 oidnum = tcp_total; 171 } 172 173 oid = tcpoids; 174 for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len); 175 ptr->xig_len > sizeof(struct xinpgen); 176 ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) { 177 tp = (struct xtcpcb *)ptr; 178 if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen || 179 (tp->xt_inp.inp_vflag & INP_IPV4) == 0) 180 continue; 181 oid->tp = tp; 182 oid->index.len = 10; 183 inaddr = ntohl(tp->xt_inp.inp_laddr.s_addr); 184 oid->index.subs[0] = (inaddr >> 24) & 0xff; 185 oid->index.subs[1] = (inaddr >> 16) & 0xff; 186 oid->index.subs[2] = (inaddr >> 8) & 0xff; 187 oid->index.subs[3] = (inaddr >> 0) & 0xff; 188 oid->index.subs[4] = ntohs(tp->xt_inp.inp_lport); 189 inaddr = ntohl(tp->xt_inp.inp_faddr.s_addr); 190 oid->index.subs[5] = (inaddr >> 24) & 0xff; 191 oid->index.subs[6] = (inaddr >> 16) & 0xff; 192 oid->index.subs[7] = (inaddr >> 8) & 0xff; 193 oid->index.subs[8] = (inaddr >> 0) & 0xff; 194 oid->index.subs[9] = ntohs(tp->xt_inp.inp_fport); 195 oid++; 196 } 197 198 qsort(tcpoids, tcp_total, sizeof(tcpoids[0]), tcp_compare); 199 200 return (0); 201 } 202 203 /* 204 * Scalars 205 */ 206 int 207 op_tcp(struct snmp_context *ctx __unused, struct snmp_value *value, 208 u_int sub, u_int iidx __unused, enum snmp_op op) 209 { 210 switch (op) { 211 212 case SNMP_OP_GETNEXT: 213 abort(); 214 215 case SNMP_OP_GET: 216 break; 217 218 case SNMP_OP_SET: 219 return (SNMP_ERR_NOT_WRITEABLE); 220 221 case SNMP_OP_ROLLBACK: 222 case SNMP_OP_COMMIT: 223 abort(); 224 } 225 226 if (tcp_stats_tick < this_tick) 227 if (fetch_tcp_stats() == -1) 228 return (SNMP_ERR_GENERR); 229 230 switch (value->var.subs[sub - 1]) { 231 232 case LEAF_tcpRtoAlgorithm: 233 value->v.integer = 4; /* Van Jacobson */ 234 break; 235 236 case LEAF_tcpRtoMin: 237 value->v.integer = tcp_rexmit_min; 238 break; 239 240 case LEAF_tcpRtoMax: 241 value->v.integer = tcp_rexmit_max; 242 break; 243 244 case LEAF_tcpMaxConn: 245 value->v.integer = -1; 246 break; 247 248 case LEAF_tcpActiveOpens: 249 value->v.uint32 = tcpstat.tcps_connattempt; 250 break; 251 252 case LEAF_tcpPassiveOpens: 253 value->v.uint32 = tcpstat.tcps_accepts; 254 break; 255 256 case LEAF_tcpAttemptFails: 257 value->v.uint32 = tcpstat.tcps_conndrops; 258 break; 259 260 case LEAF_tcpEstabResets: 261 value->v.uint32 = tcpstat.tcps_drops; 262 break; 263 264 case LEAF_tcpCurrEstab: 265 value->v.uint32 = tcps_states[TCPS_ESTABLISHED] + 266 tcps_states[TCPS_CLOSE_WAIT]; 267 break; 268 269 case LEAF_tcpInSegs: 270 value->v.uint32 = tcpstat.tcps_rcvtotal; 271 break; 272 273 case LEAF_tcpOutSegs: 274 value->v.uint32 = tcpstat.tcps_sndtotal - 275 tcpstat.tcps_sndrexmitpack; 276 break; 277 278 case LEAF_tcpRetransSegs: 279 value->v.uint32 = tcpstat.tcps_sndrexmitpack; 280 break; 281 282 case LEAF_tcpInErrs: 283 value->v.uint32 = tcpstat.tcps_rcvbadsum + 284 tcpstat.tcps_rcvbadoff + 285 tcpstat.tcps_rcvshort; 286 break; 287 } 288 return (SNMP_ERR_NOERROR); 289 } 290 291 int 292 op_tcpconn(struct snmp_context *ctx __unused, struct snmp_value *value, 293 u_int sub, u_int iidx __unused, enum snmp_op op) 294 { 295 u_int i; 296 297 if (tcp_tick < this_tick) 298 if (fetch_tcp() == -1) 299 return (SNMP_ERR_GENERR); 300 301 switch (op) { 302 303 case SNMP_OP_GETNEXT: 304 for (i = 0; i < tcp_total; i++) 305 if (index_compare(&value->var, sub, &tcpoids[i].index) < 0) 306 break; 307 if (i == tcp_total) 308 return (SNMP_ERR_NOSUCHNAME); 309 index_append(&value->var, sub, &tcpoids[i].index); 310 break; 311 312 case SNMP_OP_GET: 313 for (i = 0; i < tcp_total; i++) 314 if (index_compare(&value->var, sub, &tcpoids[i].index) == 0) 315 break; 316 if (i == tcp_total) 317 return (SNMP_ERR_NOSUCHNAME); 318 break; 319 320 case SNMP_OP_SET: 321 return (SNMP_ERR_NOT_WRITEABLE); 322 323 case SNMP_OP_ROLLBACK: 324 case SNMP_OP_COMMIT: 325 default: 326 abort(); 327 } 328 329 switch (value->var.subs[sub - 1]) { 330 331 case LEAF_tcpConnState: 332 switch (tcpoids[i].tp->t_state) { 333 334 case TCPS_CLOSED: 335 value->v.integer = 1; 336 break; 337 case TCPS_LISTEN: 338 value->v.integer = 2; 339 break; 340 case TCPS_SYN_SENT: 341 value->v.integer = 3; 342 break; 343 case TCPS_SYN_RECEIVED: 344 value->v.integer = 4; 345 break; 346 case TCPS_ESTABLISHED: 347 value->v.integer = 5; 348 break; 349 case TCPS_CLOSE_WAIT: 350 value->v.integer = 8; 351 break; 352 case TCPS_FIN_WAIT_1: 353 value->v.integer = 6; 354 break; 355 case TCPS_CLOSING: 356 value->v.integer = 10; 357 break; 358 case TCPS_LAST_ACK: 359 value->v.integer = 9; 360 break; 361 case TCPS_FIN_WAIT_2: 362 value->v.integer = 7; 363 break; 364 case TCPS_TIME_WAIT: 365 value->v.integer = 11; 366 break; 367 default: 368 value->v.integer = 0; 369 break; 370 } 371 break; 372 373 case LEAF_tcpConnLocalAddress: 374 value->v.ipaddress[0] = tcpoids[i].index.subs[0]; 375 value->v.ipaddress[1] = tcpoids[i].index.subs[1]; 376 value->v.ipaddress[2] = tcpoids[i].index.subs[2]; 377 value->v.ipaddress[3] = tcpoids[i].index.subs[3]; 378 break; 379 380 case LEAF_tcpConnLocalPort: 381 value->v.integer = tcpoids[i].index.subs[4]; 382 break; 383 384 case LEAF_tcpConnRemAddress: 385 value->v.ipaddress[0] = tcpoids[i].index.subs[5]; 386 value->v.ipaddress[1] = tcpoids[i].index.subs[6]; 387 value->v.ipaddress[2] = tcpoids[i].index.subs[7]; 388 value->v.ipaddress[3] = tcpoids[i].index.subs[8]; 389 break; 390 391 case LEAF_tcpConnRemPort: 392 value->v.integer = tcpoids[i].index.subs[9]; 393 break; 394 } 395 return (SNMP_ERR_NOERROR); 396 } 397