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