1 /* brpvcd.c -- daemon for managing RFC2684 bridge-encapsulation VCs
3 written by Chuck Musser <chuckie@well.com>
4 based on br2684ctl, by Marcel GAL <cell@sch.bme.hu>
7 #include <stdio.h> /* printf, read, write, etc. */
8 #include <stdlib.h> /* malloc, exit, etc. */
9 #include <stdarg.h> /* var args support. */
10 #include <unistd.h> /* close, unlink, etc. */
11 #include <syslog.h> /* System logging interface */
12 #include <errno.h> /* system errors */
13 #include <sys/un.h> /* unix domain sockets */
14 #include <sys/stat.h> /* stat, chmod */
15 #include <sys/types.h> /* u_int32_t, etc. */
16 #include <sys/ioctl.h> /* ioctl, etc. */
18 #include "atm.h" /* general ATM stuff */
19 #include "atmdev.h" /* ATM device ioctls */
20 #include "atmbr2684.h" /* ATM bridging structs */
21 #include "atmrt2684.h" /* ATM bridging structs */
28 struct be_group group_head;
29 struct be_memstat memstat = {0,0,0,0};
31 void do_error (int priority, const char *fmt, ...)
37 vsnprintf(buf,sizeof(buf),fmt,args);
41 if(priority == LOG_ERR) {
42 unlink(BRPVC_SOCKPATH);
48 setIndexName(char * str, const char * hdr, int index) {
52 num2 = index - OFFSET*num1;
53 sprintf(str, "%s_%d_%d", hdr, num1, num2);
57 int create_br(int nas_idx, int mode)
58 //int create_br(int nas_idx)
61 struct atm_newif_br2684 ni;
63 struct atm_newif_rt2684 ni_rt;
66 if((sock = socket(PF_ATMPVC, SOCK_DGRAM, ATM_AAL5)) < 0)
68 do_error(LOG_WARNING, "socket creation failed for nas%d: %s",nas_idx,strerror(errno));
72 /* Create the the bridge-encapsulation interface.
76 ni.backend_num = ATM_BACKEND_BR2684;
77 ni.media = BR2684_MEDIA_ETHERNET;
80 setIndexName(ni.ifname, "nas", nas_idx);
81 //sprintf(ni.ifname, "nas_%d", nas_idx);
83 ni_rt.backend_num = ATM_BACKEND_RT2684;
85 setIndexName(ni_rt.ifname, "ipa", nas_idx);
86 //sprintf(ni_rt.ifname, "atm_%d", nas_idx);
90 ni.backend_num = ATM_BACKEND_BR2684;
91 ni.media = BR2684_MEDIA_ETHERNET;
93 sprintf(ni.ifname, "nas%d", nas_idx);
97 ret=ioctl (sock, ATM_NEWBACKENDIF, &ni);
99 ret=ioctl (sock, ATM_NEWBACKENDIF, &ni_rt);
102 if(errno == EEXIST) {
104 /* It's not fatal to create an interface that already exists.
105 We probably will end up doing it all the time because there's
106 no way to delete interfaces. Not a problem.
108 /* do_error(LOG_INFO, "Interface %s already exists", ni.ifname); */
112 do_error(LOG_WARNING, "Can't create interface : %s", strerror(errno));
115 do_error(LOG_INFO, "Interface \"%s\" created sucessfully\n",mode? ni_rt.ifname:ni.ifname);
124 int assign_vcc(struct sockaddr_atmpvc addr, int nas_idx, int encap, int bufsize, int proto_filter, int mode, \
125 unsigned short vlan_id)
126 //int assign_vcc(struct sockaddr_atmpvc addr, int nas_idx, int encap, int bufsize)
130 struct atm_backend_br2684 be;
132 struct atm_backend_rt2684 be_rt;
135 if ((fd = socket(PF_ATMPVC, SOCK_DGRAM, ATM_AAL5)) < 0) {
136 do_error(LOG_WARNING,"failed to create socket, reason: %s", strerror(errno));
140 memset(&qos, 0, sizeof(qos));
142 qos.txtp.traffic_class = ATM_UBR;
143 qos.txtp.max_sdu = 1524;
144 qos.txtp.pcr = ATM_MAX_PCR;
147 if( setsockopt(fd,SOL_SOCKET,SO_SNDBUF, &bufsize ,sizeof(bufsize)) < 0) {
148 do_error(LOG_WARNING,"setsockopt SO_SNDBUF: (%d) %s\n",bufsize, strerror(errno));
152 if( setsockopt(fd, SOL_ATM, SO_ATMQOS, &qos, sizeof(qos)) < 0) {
153 do_error(LOG_WARNING,"setsockopt SO_ATMQOS %s", strerror(errno));
157 if( connect(fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_atmpvc)) < 0) {
158 do_error(LOG_WARNING,"failed to connect on socket: %s", strerror(errno));
162 /* attach the vcc to device.
167 be.backend_num = ATM_BACKEND_BR2684;
168 be.ifspec.method = BR2684_FIND_BYIFNAME;
170 setIndexName(be.ifspec.spec.ifname, "nas", nas_idx);
171 //sprintf(be.ifspec.spec.ifname, "nas_%d", nas_idx);
172 be.fcs_in = BR2684_FCSIN_NO;
173 be.fcs_out = BR2684_FCSOUT_NO;
180 be.proto_filter = proto_filter;
181 be.vlan_id = vlan_id;
185 be_rt.backend_num = ATM_BACKEND_RT2684;
186 be_rt.ifspec.method = BR2684_FIND_BYIFNAME;
188 setIndexName(be_rt.ifspec.spec.ifname, "ipa", nas_idx);
189 //sprintf(be_rt.ifspec.spec.ifname, "atm_%d", nas_idx);
190 be_rt.encaps = encap;
195 ret = ioctl (fd, ATM_SETBACKEND, &be);
197 ret = ioctl (fd, ATM_SETBACKEND, &be_rt);
199 do_error(LOG_INFO,"Communicating over ATM %d.%d.%d, encapsulation: %s\n",
203 encap ? "LLC" : "VC mux");
206 do_error(LOG_WARNING,"Could not configure interface:%s",strerror(errno));
212 void do_add(int cli_fd, struct be_msg *rmsg, struct ucred *cli_cred)
214 static struct be_group *group = &group_head;
215 static struct be_vc *prev_vc = NULL;
216 static char curr_groupname[MAX_GROUPNAME_LEN] = {};
217 static pid_t curr_cli_pid = 0;
219 struct be_group *last_group = NULL, *new_group;
220 struct be_vc *new_vc;
224 /* New client instance means that we reset the current working group.
226 if(curr_cli_pid != cli_cred->pid) {
227 curr_groupname[0] = '\0';
229 curr_cli_pid = cli_cred->pid;
233 if(create_br(rmsg->nas_idx, rmsg->mode) == 0) {
234 // if(create_br(rmsg->nas_idx) == 0) {
236 smsg.msgtype = INTERFACE_FAILED;
237 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
238 do_error(LOG_WARNING,"do_add() couldn't send interface creation failure message.");
243 if( (sock = assign_vcc(rmsg->pvc, rmsg->nas_idx, rmsg->encap, 8192, rmsg->proto_filter, rmsg->mode, \
244 rmsg->vlan_id)) < 0) {
245 // if( (sock = assign_vcc(rmsg->pvc, rmsg->nas_idx, BR2684_ENCAPS_LLC, 8192)) < 0) {
247 smsg.msgtype = SOCK_FAILED;
248 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
249 do_error(LOG_WARNING,"do_add() couldn't send socket creation failure message.");
253 /* See if this group is different from the last one. If isn't, we simply pick up where
254 we left off last time with the group and vc pointers stored in static variables.
255 This is more efficient for multiple adds, because we always want go to the end of
256 the current group's VC list.
258 if( strncmp(rmsg->name,curr_groupname,MAX_GROUPNAME_LEN) ) {
260 /* This groupname is different from the last one. Remember it for next time.
263 strncpy(curr_groupname,rmsg->name,MAX_GROUPNAME_LEN);
265 /* Does this group exist?
267 for(group = &group_head; group != NULL; group = group->next) {
269 if(!strncmp(rmsg->name,group->name,MAX_GROUPNAME_LEN))
275 /* Nope. Create a new group.
277 if(! (new_group = (struct be_group *)malloc(sizeof(struct be_group))) ) {
278 do_error(LOG_WARNING,"can't allocate memory for new group %s", rmsg->name);
281 smsg.msgtype = NOMEM;
282 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
283 do_error(LOG_WARNING,"do_add() couldn't send malloc failure message for new group.");
287 memstat.group_mallocs++;
289 last_group->next = new_group;
290 new_group->head = NULL;
291 new_group->next = NULL;
292 strncpy(new_group->name,rmsg->name,MAX_GROUPNAME_LEN);
294 /* set prev_vc to the VC list head.
297 prev_vc = group->head;
300 /* Ratchet to the end of an existing group VC list.
302 for(prev_vc = group->head; prev_vc->next != NULL; prev_vc=prev_vc->next);
307 if(! (new_vc = (struct be_vc *)malloc(sizeof(struct be_vc))) ) {
308 do_error(LOG_WARNING,"can't allocate memory for new vc %d/%d on nas%d\n",
309 rmsg->pvc.sap_addr.vpi,
310 rmsg->pvc.sap_addr.vci,
315 smsg.msgtype = NOMEM;
316 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
317 do_error(LOG_WARNING,"do_add() couldn't send malloc failure message for new VC.");
321 memstat.vc_mallocs++;
323 new_vc->nas_idx = rmsg->nas_idx;
324 memcpy(&new_vc->pvc, &rmsg->pvc, sizeof(struct sockaddr_atmpvc));
326 new_vc->uid = cli_cred->uid;
327 new_vc->vlan_id = rmsg->vlan_id;
330 /* Add the new VC to the end of the list and remember the
331 new end for next time. This "if" block is neccessary because
332 we cannot initialize prev_vc with the address of group_head.pvc,
333 which is non-constant. My intent is to keep most of the state
334 variables local to this function.
338 group->head = new_vc;
340 prev_vc->next = new_vc;
345 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
346 do_error(LOG_ERR,"do_add couldn't send OK response: %s",strerror(errno));
350 int delete_br(int num)
351 //int delete_br(char *nstr)
357 sock = socket(PF_ATMPVC, SOCK_DGRAM, ATM_AAL5);
359 syslog(LOG_ERR, "socket creation failed: %s",strerror(errno));
361 /* create the device with ioctl: */
363 if( num>=0 && num<1234567890){
364 struct atm_newif_br2684 ni;
365 ni.backend_num = ATM_BACKEND_BR2684;
366 ni.media = BR2684_MEDIA_ETHERNET;
368 setIndexName(ni.ifname, "nas", num);
369 //sprintf(ni.ifname, "nas_%d", num);
370 err=ioctl (sock, BR2684_DEL, &ni);
372 syslog(LOG_ERR,"err: strange interface number %d", num );
379 int vc_match(struct be_vc *vc, struct be_msg *msg)
381 if(vc == NULL) return 0;
383 if( vc->pvc.sap_addr.vpi == msg->pvc.sap_addr.vpi &&
384 vc->pvc.sap_addr.vci == msg->pvc.sap_addr.vci &&
385 vc->nas_idx == msg->nas_idx
392 void do_delete(int cli_fd, struct be_msg *rmsg, struct ucred *cli_cred)
394 static struct be_vc placeholder, *bookmark = NULL;
395 static struct be_group *group = NULL, *prev_group = &group_head;
396 static pid_t curr_cli_pid;
398 struct be_vc *prev = NULL, *match = NULL, *new_next, *doomed;
402 /* New client instance means that we reset the current bookmark.
404 if(curr_cli_pid != cli_cred->pid) {
406 curr_cli_pid = cli_cred->pid;
409 if(bookmark == NULL || (vc_match(bookmark->next,rmsg) == 0)) {
411 /* No current bookmark, or the VC after the bookmark doesn't match
412 the VC we want to delete. Try to find the VC somewhere.
415 for(group = &group_head; group != NULL ; group = group->next) {
416 for(match = group->head; match != NULL; match = match->next) {
417 if((search_done = vc_match(match,rmsg))) break;
420 if(search_done) break;
426 /* VC not found, invalidate the bookmark, send fail message and return.
430 smsg.msgtype = VC_NOT_FOUND;
431 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
432 do_error(LOG_ERR,"do_delete couldn't send VC_NOT_FOUND response: %s",strerror(errno));
437 /* Wrong credentials, also no dice.
439 if(match->uid != cli_cred->uid) {
441 smsg.msgtype = NOT_OWNER;
442 smsg.uid = bookmark->next->uid;
444 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
445 do_error(LOG_ERR,"do_delete couldn't send NOT_OWNER response: %s", strerror(errno));
449 if(match == group->head) {
451 /* VC to be deleted is at the beginning of the group list. Set the bookmark
452 to our static placeholder, update its next member, and move the group head
453 to the next VC in the list.
456 if(match->next == NULL) {
458 /* Special case: if only one VC remains in the list, free the group if
462 if(group == &group_head) {
465 prev_group->next = group->next;
467 memstat.group_frees++;
471 bookmark = &placeholder;
472 group->head = match->next;
473 bookmark->next = match->next;
475 } else if(match->next == NULL) {
477 /* VC to be deleted is at the end of the list. Invalidate the bookmark
478 so we don't bother picking up where we left off next time. Also, update
479 the new last item in the list (prev) so that its next member points to NULL.
487 /* VC to be deleted is somewhere in the middle of the list. Set the bookmark
488 to the previous VC, and set that VC's next member to the VC following the
489 one we'll be deleting.
493 bookmark->next = match->next;
496 /* In all cases, close the socket related to the VC we want to delete,
497 free the memory, increment our vc_frees counter and send the OK message
502 delete_br(match->nas_idx);
509 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
510 do_error(LOG_ERR,"no bookmark: do_delete couldn't send OK response: %s",strerror(errno));
513 } else { /* The bookmark is usable because its next member matches
514 the VC we want to delete.
517 if(bookmark->next->uid != cli_cred->uid) {
519 /* Wrong credentials, no dice.
522 smsg.msgtype = NOT_OWNER;
523 smsg.uid = bookmark->next->uid;
525 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
526 do_error(LOG_ERR,"using bookmark: do_delete couldn't send NOT_OWNER response: %s",strerror(errno));
531 /* Remember the VC after the one we're going to delete.
533 new_next = bookmark->next->next;
535 if(bookmark->next == group->head) {
537 /* If we're at the beginning of the list, there are two special cases.
540 if(group->head->next == NULL) {
542 /* If only one VC remains in the list, free the group structure
543 (if we allocated it) and invalidate the bookmark.
546 doomed = group->head;
548 /* Don't free the global group list head!
550 if(group == &group_head) {
553 prev_group->next = group->next;
555 memstat.group_frees++;
562 /* If there is more than one VC in the list, we update the group head,
563 AND update the bookmark.
566 doomed = bookmark->next;
567 group->head = new_next;
568 bookmark->next = new_next;
571 } else if(new_next == NULL) {
573 /* If the VC to be deleted is the last one, we set the bookmark VC's next pointer
574 to NULL, and then invalidate the bookmark.
577 doomed = bookmark->next;
578 bookmark->next = NULL;
583 /* If the VC to be deleted is somewhere in the middle, then all we do is update
584 the bookmark and do the usual VC delete actions.
587 doomed = bookmark->next;
588 bookmark->next = new_next;
593 delete_br(doomed->nas_idx);
600 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
601 do_error(LOG_ERR,"do_delete couldn't send OK response: %s",strerror(errno));
606 void do_delete_group(int cli_fd, char *name, struct ucred *cli_cred)
608 struct be_group *group, *prev_group = NULL;
609 struct be_vc *curr,*doomed;
612 smsg.msgtype = GROUP_NOT_FOUND;
614 for(group = &group_head; group != NULL; group = group->next) {
615 if(!strncmp(name,group->name,MAX_GROUPNAME_LEN))
622 while(curr != NULL) {
627 delete_br(doomed->nas_idx);
634 prev_group->next = group->next;
636 memstat.group_frees++;
640 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
641 do_error(LOG_ERR,"do_delete_group() couldn't send response: %s",strerror(errno));
645 void do_list_group(int cli_fd, struct be_msg *rmsg)
647 struct be_group *group;
651 smsg.msgtype = GROUP_NOT_FOUND;
653 for(group = &group_head; group != NULL; group = group->next)
654 if(!strncmp(rmsg->name,group->name,MAX_GROUPNAME_LEN))
658 for(curr = group->head; curr != NULL; curr = curr->next) {
661 smsg.nas_idx = curr->nas_idx;
662 smsg.uid = curr->uid;
663 memcpy(&smsg.pvc, &curr->pvc, sizeof(struct sockaddr_atmpvc));
664 memcpy(&smsg.name, &group->name, MAX_GROUPNAME_LEN);
665 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
666 do_error(LOG_ERR,"do_list_group() couldn't send OK message: %s",strerror(errno));
669 smsg.msgtype = LIST_END;
672 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
673 do_error(LOG_ERR,"do_list_group() couldn't send LIST_END message: %s",strerror(errno));
677 void do_list_all(int cli_fd, struct be_msg *rmsg)
679 struct be_group *group = NULL;
683 for(group = &group_head; group != NULL; group = group->next) {
685 for(curr = group->head; curr != NULL; curr = curr->next) {
688 smsg.nas_idx = curr->nas_idx;
689 smsg.uid = curr->uid;
690 memcpy(&smsg.pvc, &curr->pvc, sizeof(struct sockaddr_atmpvc));
691 memcpy(&smsg.name, group->name, MAX_GROUPNAME_LEN);
693 smsg.mode = curr->mode;
694 smsg.vlan_id = curr->vlan_id;
696 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
697 do_error(LOG_ERR,"do_list_all() couldn't send VC info: %s",strerror(errno));
702 if(smsg.msgtype == OK)
703 smsg.msgtype = LIST_END;
705 smsg.msgtype = GROUP_NOT_FOUND;
707 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
708 do_error(LOG_ERR,"do_list_all() couldn't send LIST_END message: %s",strerror(errno));
712 int pvc2684d_main (int argc, char **argv)
714 int main (int argc, char **argv)
717 int test_fd, listen_fd, cli_fd;
718 socklen_t cliaddr_len;
719 struct sockaddr_un test_addr, listen_addr, cli_addr;
722 struct ucred cli_cred;
723 int ucred_len = sizeof(cli_cred);
724 struct be_msg smsg, rmsg;
725 struct stat listen_stat;
727 openlog("pvc2684d",LOG_PERROR, LOG_DAEMON);
729 bzero(group_head.name,MAX_GROUPNAME_LEN);
730 group_head.head = NULL;
731 group_head.next = NULL;
733 bzero(&test_addr, sizeof(test_addr));
734 test_addr.sun_family = AF_LOCAL;
735 strncpy(test_addr.sun_path, BRPVC_SOCKPATH, sizeof(test_addr.sun_path) -1);
736 memcpy(&listen_addr, &test_addr, sizeof(test_addr));
738 if( (test_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
739 do_error(LOG_ERR,"Couldn't create initial socket: %s",strerror(errno));
741 /* Check for already running daemon */
743 if(connect(test_fd, (struct sockaddr *) &test_addr, sizeof(test_addr))) {
744 if(errno == ECONNREFUSED)
745 unlink(BRPVC_SOCKPATH);
749 if( (listen_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
750 do_error(LOG_ERR,"Couldn't create server socket: %s",strerror(errno));
752 if( bind(listen_fd, (struct sockaddr *) &listen_addr, SUN_LEN(&listen_addr)) ) {
753 do_error(LOG_WARNING,"Another b2684d is running");
758 if(stat(BRPVC_SOCKPATH, &listen_stat))
759 do_error(LOG_ERR,"Can't fstat listen socket: %s",strerror(errno));
761 if(chmod(BRPVC_SOCKPATH, listen_stat.st_mode | S_IWOTH))
762 do_error(LOG_ERR,"Can't fchmod listen socket: %s",strerror(errno));
764 if( listen(listen_fd, 5) )
765 do_error(LOG_ERR,"listen() on server socket failed: %s",strerror(errno));
768 cliaddr_len = sizeof(cli_addr);
769 if((cli_fd = accept(listen_fd, (struct sockaddr *) &cli_addr, &cliaddr_len)) < 0) {
773 do_error(LOG_ERR,"accept() on server socket failed: %s",strerror(errno));
775 if( setsockopt(cli_fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0 )
776 do_error(LOG_ERR,"setsockopt() on client socket failed: %s",strerror(errno));
778 while((nbytes = recv(cli_fd, &rmsg, sizeof(rmsg), 0)) > 0) {
779 switch (rmsg.msgtype) {
783 if ( getsockopt(cli_fd, SOL_SOCKET, SO_PEERCRED, &cli_cred, &ucred_len) < 0 )
784 do_error(LOG_ERR,"getsockopt() for credentials failed: %s",strerror(errno));
787 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
788 do_error(LOG_ERR,"Couldn't send OK message to new client: %s",strerror(errno));
794 do_add(cli_fd,&rmsg,&cli_cred);
799 do_delete(cli_fd,&rmsg,&cli_cred);
804 do_delete_group(cli_fd,rmsg.name,&cli_cred);
809 do_list_group(cli_fd,&rmsg);
814 do_list_all(cli_fd,&rmsg);
819 if( send(cli_fd, &memstat, sizeof(memstat), 0) < 0 )
820 do_error(LOG_ERR,"Couldn't send MEM_STAT message: %s",strerror(errno));
824 smsg.msgtype = UNKNOWN_CMD;
825 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
826 do_error(LOG_ERR,"Couldn't send UNKNOWN_COMMAND message: %s",strerror(errno));