2 * Copyright (C) 2007 - Filippo Giunchedi
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License
5 * as published by the Free Software Foundation; either version 2
6 * of the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #include <sys/socket.h>
29 #include <vdecommon.h>
31 #include <libvdesnmp.h>
32 #include <libvdemgmt.h>
33 #include <vdeplugin.h>
36 #define EXIT(i) exit(i)
38 #define EXIT(i) return(i)
41 vde_stats_t
*_stats
= NULL
;
43 struct vdemgmt
*mgmt_conn
;
44 struct vdemgmt_out
*mgmt_outbuf
;
46 struct timeval
*init_tv
;
47 struct timeval
*cur_tv
;
49 int (*events
[EVENTS_NUM
])(int);
52 assert( _stats
== NULL
);
55 _stats
= malloc(sizeof(vde_stats_t
));
65 #define PORTPRINT(pl) debug(" port: %d", pl->index); \
66 debug(" desc: %s", pl->desc); \
67 debug(" mtu: %d", pl->mtu); \
68 debug(" speed: %d", pl->speed); \
69 debug(" phyaddr: %s", pl->phyaddress); \
70 debug(" adminstatus: %d", pl->adminstatus); \
71 debug(" operstatus: %d", pl->operstatus); \
72 debug(" lastchange: %ld", pl->time_lastchange); \
73 debug(" in->ucastpkts: %ld", pl->in->ucastpkts); \
74 debug(" in->octects: %ld", pl->in->octects); \
75 debug(" out->ucastpkts: %ld", pl->out->ucastpkts); \
76 debug(" out->octects: %ld", pl->out->octects);
78 /* ths of second between a and b (both struct timeval*) assuming a > b */
79 #define CSECDIFF(a, b) ( (((a)->tv_sec - (b)->tv_sec) * 100) + (( (a)->tv_usec > (b)->tv_usec ? (a)->tv_usec - (b)->tv_usec : 1000000 - (b)->tv_usec + (a)->tv_usec ) / 10000 ) )
81 /* return ths of a second from init_tv */
82 #define CSECINIT() ( CSECDIFF(cur_tv, init_tv) )
84 #define PORTUP(num) if( _stats->ports[num].operstatus != OPERSTATUS_UP ) { \
85 _stats->ports[num].time_lastchange = CSECINIT(); } \
86 debug("portup: %d", num); \
87 _stats->ports[num].adminstatus = ADMINSTATUS_UP; \
88 _stats->ports[num].operstatus = OPERSTATUS_UP; \
89 _stats->ports[num].active = 1;
91 #define PORTDOWN(num) if( _stats->ports[num].operstatus != OPERSTATUS_DOWN ) { \
92 _stats->ports[num].time_lastchange = CSECINIT(); } \
93 debug("portdown: %d", num); \
94 _stats->ports[num].adminstatus = ADMINSTATUS_DOWN; \
95 _stats->ports[num].operstatus = OPERSTATUS_DOWN; \
96 _stats->ports[num].active = 0;
100 #define SENDCMD(cmd) memset(mgmt_outbuf, 0, sizeof(struct vdemgmt_out)); if(!mgmt_conn) { errno = ECONNREFUSED; return 0; } vdemgmt_sendcmd(mgmt_conn, cmd, mgmt_outbuf);
102 int mgmt_init(char *sockpath
){
104 short countersok
=0, numportsok
=0;
106 mgmt_conn
= vdemgmt_open(sockpath
);
109 errno
= ECONNREFUSED
;
113 mgmt_outbuf
=(struct vdemgmt_out
*)malloc(sizeof(struct vdemgmt_out
));
119 SENDCMD("port/showinfo");
121 // FIXME this could be factored into a macro
122 q
=p
=mgmt_outbuf
->buf
;
123 while(p
< mgmt_outbuf
->buf
+mgmt_outbuf
->sz
){
125 if( strcmp(q
, "counters=true\n") == 0 )
128 if( sscanf(q
, "Numports=%d\n", &(_stats
->numports
)) == 1 )
136 if( countersok
&& numportsok
)
139 printf("couldn't parse counters or numports\n");
143 int ports_init(void){
145 struct vde_port_stats
*ps
;
147 cur_tv
= malloc(sizeof(struct timeval
));
148 init_tv
= malloc(sizeof(struct timeval
));
150 assert(_stats
!= NULL
);
151 assert(_stats
->numports
> 0);
153 _stats
->ports
= (struct vde_port_stats
*) malloc(sizeof(struct vde_port_stats
) * _stats
->numports
);
155 assert(_stats
->ports
!= NULL
);
157 // ASSUMPTION: this is the same as sysUpTime time
158 gettimeofday(init_tv
, NULL
);
160 for(i
=0; i
<_stats
->numports
; i
++){
161 ps
= &(_stats
->ports
[i
]);
162 ps
->out
= malloc(sizeof(traffic_t
));
163 ps
->in
= malloc(sizeof(traffic_t
));
164 assert( ps
->in
!= NULL
&& ps
->out
!= NULL
);
168 // FIXME what sensible values might be for mtu/speed?
172 ps
->adminstatus
= ADMINSTATUS_DOWN
;
173 ps
->operstatus
= OPERSTATUS_NOTPRESENT
;
174 // TimeTicks == hundredths of a second
175 ps
->time_lastchange
= init_tv
->tv_usec
;
178 ps
->phyaddress
[0] = '\0';
181 ps
->in
->ucastpkts
= 0;
182 ps
->in
->discards
= 0;
184 ps
->in
->unknownprotos
= 0;
186 ps
->out
->octects
= 0;
187 ps
->out
->ucastpkts
= 0;
188 ps
->out
->discards
= 0;
190 ps
->out
->unknownprotos
= 0;
195 // FIXME mac address info from hash/print is missing
196 // Hash: 0105 Addr: ae:4a:3c:e1:6e:c9 VLAN 0000 to port: 001 age 3 secs
197 int counters_parse(void){
202 char portdesc
[DESC_MAXLEN
];
204 short inport
=0, outok
=0, inok
=0;
206 struct vde_port_stats
*pl
;
208 // FIXME are these types large enough?
209 long inbytes
, inpkts
;
210 long outbytes
, outpkts
;
212 memset(portdesc
, '\0', DESC_MAXLEN
);
215 printf("error initializing connection, is vde running?\n");
219 assert(_stats
->ports
!= NULL
);
221 for(i
=0; i
< _stats
->numports
; i
++){
222 _stats
->ports
[i
].active
= 0;
225 SENDCMD("port/allprint");
227 q
=p
=mgmt_outbuf
->buf
;
228 while(p
< mgmt_outbuf
->buf
+mgmt_outbuf
->sz
){
231 /* Port 0001 untagged_vlan=0000 INACTIVE - Unnamed Allocatable */
232 if( sscanf(q
, "Port %4d %*s %s - %*s\n", &curport
, portstatus
) == 2 )
236 if( sscanf(q
, " IN: pkts %ld bytes %ld\n", &inpkts
, &inbytes
) == 2 )
239 if( sscanf(q
, " OUT: pkts %ld bytes %ld\n", &outpkts
, &outbytes
) == 2 )
242 /* -- endpoint ID 0005 module unix prog : vde_plug: user=godog PID=22006 SOCK=/tmp/vde.ctl.22006-00000 */
243 /* format from port.c:print_port() however there's room for DESC_MAXLEN bytes in portdesc */
244 if( (sscanf(q
, " -- endpoint ID %*04d module %*12c: %255c\n", portdesc
) == 1) ||
245 ( (strncmp(portstatus
, "INACTIVE", 8) == 0) && inok
&& outok
) ){
247 gettimeofday(cur_tv
, NULL
);
249 pl
= &(_stats
->ports
[curport
-1]);
253 pl
->in
->octects
= inbytes
;
254 pl
->in
->ucastpkts
= inpkts
;
255 pl
->out
->octects
= outbytes
;
256 pl
->out
->ucastpkts
= outpkts
;
258 // FIXME we do not (yet) know the admin status since it is the
259 // "preferred status", i.e. the one wanted by user
260 if( strncmp(portstatus
, "INACTIVE", 8) == 0 ){
262 } else if( strncmp(portstatus
, "ACTIVE", 6) == 0 ){
264 strncpy(pl
->desc
, portdesc
, strlen(portdesc
)-1);
267 inpkts
= inbytes
= outpkts
= outbytes
= 0;
276 } /* if(*p == '\0') */
278 } /* while(p < mgmt_outbuf->buf+mgmt_outbuf->sz){ */
283 void port_debug_handler(const char *event
, const int tag
, const char *data
){
286 char tmpstr
[DESC_MAXLEN
];
288 memset(tmpstr
, '\0', DESC_MAXLEN
);
290 gettimeofday(cur_tv
, NULL
);
292 //printf("received: %s -- %d -- %s\n", event, tag, data);
296 if( sscanf(data
, "/descr Port %02d", &portnum
) == 1 ){
297 debug("parsed port %d\n", portnum
);
300 i
= index(data
, '"');
301 j
= rindex(data
, '"');
302 if( i
&& j
&& j
> i
&& portnum
){
303 strncpy(tmpstr
, i
+1, j
- i
);
304 strncpy(_stats
->ports
[portnum
-1].desc
, tmpstr
, DESC_MAXLEN
);
306 debug("parsed descr[%p %p]: %s", i
, j
, tmpstr
);
310 debug("ENDPOINT MINUS\n");
311 if( sscanf(data
, "ep/- Port %02d", &portnum
) == 1 ){
313 if(events
[EVENT_PORT_DOWN
])
314 events
[EVENT_PORT_DOWN
](portnum
-1);
319 debug("ENDPOINT PLUS\n");
320 if( sscanf(data
, "ep/+ Port %02d", &portnum
) == 1 ){
322 if(events
[EVENT_PORT_UP
])
323 events
[EVENT_PORT_UP
](portnum
-1);
328 debug("PORT MINUS\n");
329 if( sscanf(data
, "/- %02d", &portnum
) == 1 ){
335 debug("PORT PLUS\n");
336 if( sscanf(data
, "/+ %02d", &portnum
) == 1 ){
343 int vde_snmp_reset_lastchange(){
344 return gettimeofday(init_tv
, NULL
);
347 int vde_snmp_update(){
348 return counters_parse();
351 int vde_snmp_init(char *sockpath
){
354 debug("couldn't stats_init\n");
358 if( !mgmt_init(sockpath
) ){
359 debug("couldn't mgmt_init\n");
363 if( vdemgmt_asyncreg(mgmt_conn
, "port", port_debug_handler
) != 0 ){
367 events
[EVENT_PORT_UP
] = NULL
;
368 events
[EVENT_PORT_DOWN
] = NULL
;
371 debug("couldn't ports_init\n");
375 // vde_snmp_dumpstats(_stats);
380 return counters_parse();
383 /*vdemgmt_rstout(mgmt_outbuf);*/
384 /*vdemgmt_sendcmd(mgmt_conn, "debug/list", mgmt_outbuf);*/
385 /*write(1, mgmt_outbuf->buf, mgmt_outbuf->sz);*/
387 /* standalone mode, only print port events */
389 struct pollfd pfd
={vdemgmt_getfd(mgmt_conn
), POLLIN
, 0};
391 vdemgmt_asyncrecv(mgmt_conn
);
392 PORTPRINT((&(_stats
->ports
[0])));
396 // FIXME vde_snmp_close() is missing
398 vde_stats_t
* vde_snmp_get_stats(){
402 void vde_snmp_dumpstats(vde_stats_t
*stats
){
404 struct vde_port_stats
*pl
;
406 assert( stats
!= NULL
);
408 debug("numports: %d", stats
->numports
);
410 assert( stats
->ports
!= NULL
);
412 for(i
=0; i
< stats
->numports
; i
++){
413 pl
= &(stats
->ports
[i
]);
418 int vde_snmp_getfd(){
419 assert(mgmt_conn
!= NULL
);
420 return vdemgmt_getfd(mgmt_conn
);
423 void vde_snmp_event(){
424 assert(mgmt_conn
!= NULL
);
425 vdemgmt_asyncrecv(mgmt_conn
);
428 // TODO support more than one callback per event type
429 int vde_snmp_register_callback(int event
, int (*callback
)(int portindex
)){
430 if( event
< 0 || event
>= EVENTS_NUM
){
435 events
[event
] = callback
;