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