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)
215 //brcm begin: static pointers of prev_vc,group has problems when delete is called in between adds
216 // static struct be_group *group = &group_head;
217 // static struct be_vc *prev_vc = NULL;
218 struct be_group *group = &group_head;
219 struct be_vc *prev_vc = NULL;
221 static char curr_groupname[MAX_GROUPNAME_LEN] = {};
222 static pid_t curr_cli_pid = 0;
224 struct be_group *last_group = NULL, *new_group;
225 struct be_vc *new_vc;
229 /* New client instance means that we reset the current working group.
231 if(curr_cli_pid != cli_cred->pid) {
232 curr_groupname[0] = '\0';
234 curr_cli_pid = cli_cred->pid;
238 if(create_br(rmsg->nas_idx, rmsg->mode) == 0) {
239 // if(create_br(rmsg->nas_idx) == 0) {
241 smsg.msgtype = INTERFACE_FAILED;
242 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
243 do_error(LOG_WARNING,"do_add() couldn't send interface creation failure message.");
248 if( (sock = assign_vcc(rmsg->pvc, rmsg->nas_idx, rmsg->encap, 8192, rmsg->proto_filter, rmsg->mode, \
249 rmsg->vlan_id)) < 0) {
250 // if( (sock = assign_vcc(rmsg->pvc, rmsg->nas_idx, BR2684_ENCAPS_LLC, 8192)) < 0) {
252 smsg.msgtype = SOCK_FAILED;
253 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
254 do_error(LOG_WARNING,"do_add() couldn't send socket creation failure message.");
258 /* See if this group is different from the last one. If isn't, we simply pick up where
259 we left off last time with the group and vc pointers stored in static variables.
260 This is more efficient for multiple adds, because we always want go to the end of
261 the current group's VC list.
263 if( strncmp(rmsg->name,curr_groupname,MAX_GROUPNAME_LEN) ) {
265 /* This groupname is different from the last one. Remember it for next time.
268 strncpy(curr_groupname,rmsg->name,MAX_GROUPNAME_LEN);
270 /* Does this group exist?
272 for(group = &group_head; group != NULL; group = group->next) {
274 if(!strncmp(rmsg->name,group->name,MAX_GROUPNAME_LEN))
280 /* Nope. Create a new group.
282 if(! (new_group = (struct be_group *)malloc(sizeof(struct be_group))) ) {
283 do_error(LOG_WARNING,"can't allocate memory for new group %s", rmsg->name);
286 smsg.msgtype = NOMEM;
287 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
288 do_error(LOG_WARNING,"do_add() couldn't send malloc failure message for new group.");
292 memstat.group_mallocs++;
294 last_group->next = new_group;
295 new_group->head = NULL;
296 new_group->next = NULL;
297 strncpy(new_group->name,rmsg->name,MAX_GROUPNAME_LEN);
299 /* set prev_vc to the VC list head.
302 prev_vc = group->head;
305 /* Ratchet to the end of an existing group VC list.
307 for(prev_vc = group->head; prev_vc->next != NULL; prev_vc=prev_vc->next);
312 if(! (new_vc = (struct be_vc *)malloc(sizeof(struct be_vc))) ) {
313 do_error(LOG_WARNING,"can't allocate memory for new vc %d/%d on nas%d\n",
314 rmsg->pvc.sap_addr.vpi,
315 rmsg->pvc.sap_addr.vci,
320 smsg.msgtype = NOMEM;
321 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
322 do_error(LOG_WARNING,"do_add() couldn't send malloc failure message for new VC.");
326 memstat.vc_mallocs++;
328 new_vc->nas_idx = rmsg->nas_idx;
329 memcpy(&new_vc->pvc, &rmsg->pvc, sizeof(struct sockaddr_atmpvc));
331 new_vc->uid = cli_cred->uid;
332 new_vc->vlan_id = rmsg->vlan_id;
335 /* Add the new VC to the end of the list and remember the
336 new end for next time. This "if" block is neccessary because
337 we cannot initialize prev_vc with the address of group_head.pvc,
338 which is non-constant. My intent is to keep most of the state
339 variables local to this function.
342 group->head = new_vc;
345 for(prev_vc = group->head; prev_vc->next != NULL; prev_vc=prev_vc->next) ;
348 prev_vc->next = new_vc;
351 //brcm this has a problem if the VCC in the list is deleted
355 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
356 do_error(LOG_ERR,"do_add couldn't send OK response: %s",strerror(errno));
360 int delete_br(int num)
361 //int delete_br(char *nstr)
367 sock = socket(PF_ATMPVC, SOCK_DGRAM, ATM_AAL5);
369 syslog(LOG_ERR, "socket creation failed: %s",strerror(errno));
371 /* create the device with ioctl: */
373 if( num>=0 && num<1234567890){
374 struct atm_newif_br2684 ni;
375 ni.backend_num = ATM_BACKEND_BR2684;
376 ni.media = BR2684_MEDIA_ETHERNET;
378 setIndexName(ni.ifname, "nas", num);
379 //sprintf(ni.ifname, "nas_%d", num);
380 err=ioctl (sock, BR2684_DEL, &ni);
382 syslog(LOG_ERR,"err: strange interface number %d", num );
389 int vc_match(struct be_vc *vc, struct be_msg *msg)
391 if(vc == NULL) return 0;
393 if( vc->pvc.sap_addr.vpi == msg->pvc.sap_addr.vpi &&
394 vc->pvc.sap_addr.vci == msg->pvc.sap_addr.vci &&
395 vc->nas_idx == msg->nas_idx
402 void do_delete(int cli_fd, struct be_msg *rmsg, struct ucred *cli_cred)
404 static struct be_vc placeholder, *bookmark = NULL;
405 static struct be_group *group = NULL, *prev_group = &group_head;
406 static pid_t curr_cli_pid;
408 struct be_vc *prev = NULL, *match = NULL, *new_next, *doomed;
412 /* New client instance means that we reset the current bookmark.
414 if(curr_cli_pid != cli_cred->pid) {
416 curr_cli_pid = cli_cred->pid;
419 if(bookmark == NULL || (vc_match(bookmark->next,rmsg) == 0)) {
421 /* No current bookmark, or the VC after the bookmark doesn't match
422 the VC we want to delete. Try to find the VC somewhere.
425 for(group = &group_head; group != NULL ; group = group->next) {
426 for(match = group->head; match != NULL; match = match->next) {
427 if((search_done = vc_match(match,rmsg))) break;
430 if(search_done) break;
436 /* VC not found, invalidate the bookmark, send fail message and return.
440 smsg.msgtype = VC_NOT_FOUND;
441 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
442 do_error(LOG_ERR,"do_delete couldn't send VC_NOT_FOUND response: %s",strerror(errno));
447 /* Wrong credentials, also no dice.
449 if(match->uid != cli_cred->uid) {
451 smsg.msgtype = NOT_OWNER;
452 smsg.uid = bookmark->next->uid;
454 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
455 do_error(LOG_ERR,"do_delete couldn't send NOT_OWNER response: %s", strerror(errno));
459 if(match == group->head) {
461 /* VC to be deleted is at the beginning of the group list. Set the bookmark
462 to our static placeholder, update its next member, and move the group head
463 to the next VC in the list.
466 if(match->next == NULL) {
468 /* Special case: if only one VC remains in the list, free the group if
472 if(group == &group_head) {
475 prev_group->next = group->next;
477 memstat.group_frees++;
481 bookmark = &placeholder;
482 group->head = match->next;
483 bookmark->next = match->next;
485 } else if(match->next == NULL) {
487 /* VC to be deleted is at the end of the list. Invalidate the bookmark
488 so we don't bother picking up where we left off next time. Also, update
489 the new last item in the list (prev) so that its next member points to NULL.
497 /* VC to be deleted is somewhere in the middle of the list. Set the bookmark
498 to the previous VC, and set that VC's next member to the VC following the
499 one we'll be deleting.
503 bookmark->next = match->next;
506 /* In all cases, close the socket related to the VC we want to delete,
507 free the memory, increment our vc_frees counter and send the OK message
512 delete_br(match->nas_idx);
519 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
520 do_error(LOG_ERR,"no bookmark: do_delete couldn't send OK response: %s",strerror(errno));
523 } else { /* The bookmark is usable because its next member matches
524 the VC we want to delete.
527 if(bookmark->next->uid != cli_cred->uid) {
529 /* Wrong credentials, no dice.
532 smsg.msgtype = NOT_OWNER;
533 smsg.uid = bookmark->next->uid;
535 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
536 do_error(LOG_ERR,"using bookmark: do_delete couldn't send NOT_OWNER response: %s",strerror(errno));
541 /* Remember the VC after the one we're going to delete.
543 new_next = bookmark->next->next;
545 if(bookmark->next == group->head) {
547 /* If we're at the beginning of the list, there are two special cases.
550 if(group->head->next == NULL) {
552 /* If only one VC remains in the list, free the group structure
553 (if we allocated it) and invalidate the bookmark.
556 doomed = group->head;
558 /* Don't free the global group list head!
560 if(group == &group_head) {
563 prev_group->next = group->next;
565 memstat.group_frees++;
572 /* If there is more than one VC in the list, we update the group head,
573 AND update the bookmark.
576 doomed = bookmark->next;
577 group->head = new_next;
578 bookmark->next = new_next;
581 } else if(new_next == NULL) {
583 /* If the VC to be deleted is the last one, we set the bookmark VC's next pointer
584 to NULL, and then invalidate the bookmark.
587 doomed = bookmark->next;
588 bookmark->next = NULL;
593 /* If the VC to be deleted is somewhere in the middle, then all we do is update
594 the bookmark and do the usual VC delete actions.
597 doomed = bookmark->next;
598 bookmark->next = new_next;
603 delete_br(doomed->nas_idx);
610 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
611 do_error(LOG_ERR,"do_delete couldn't send OK response: %s",strerror(errno));
616 void do_delete_group(int cli_fd, char *name, struct ucred *cli_cred)
618 struct be_group *group, *prev_group = NULL;
619 struct be_vc *curr,*doomed;
622 smsg.msgtype = GROUP_NOT_FOUND;
624 for(group = &group_head; group != NULL; group = group->next) {
625 if(!strncmp(name,group->name,MAX_GROUPNAME_LEN))
632 while(curr != NULL) {
637 delete_br(doomed->nas_idx);
644 prev_group->next = group->next;
646 memstat.group_frees++;
650 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
651 do_error(LOG_ERR,"do_delete_group() couldn't send response: %s",strerror(errno));
655 void do_list_group(int cli_fd, struct be_msg *rmsg)
657 struct be_group *group;
661 smsg.msgtype = GROUP_NOT_FOUND;
663 for(group = &group_head; group != NULL; group = group->next)
664 if(!strncmp(rmsg->name,group->name,MAX_GROUPNAME_LEN))
668 for(curr = group->head; curr != NULL; curr = curr->next) {
671 smsg.nas_idx = curr->nas_idx;
672 smsg.uid = curr->uid;
673 memcpy(&smsg.pvc, &curr->pvc, sizeof(struct sockaddr_atmpvc));
674 memcpy(&smsg.name, &group->name, MAX_GROUPNAME_LEN);
675 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
676 do_error(LOG_ERR,"do_list_group() couldn't send OK message: %s",strerror(errno));
679 smsg.msgtype = LIST_END;
682 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
683 do_error(LOG_ERR,"do_list_group() couldn't send LIST_END message: %s",strerror(errno));
687 void do_list_all(int cli_fd, struct be_msg *rmsg)
689 struct be_group *group = NULL;
693 for(group = &group_head; group != NULL; group = group->next) {
695 for(curr = group->head; curr != NULL; curr = curr->next) {
698 smsg.nas_idx = curr->nas_idx;
699 smsg.uid = curr->uid;
700 memcpy(&smsg.pvc, &curr->pvc, sizeof(struct sockaddr_atmpvc));
701 memcpy(&smsg.name, group->name, MAX_GROUPNAME_LEN);
703 smsg.mode = curr->mode;
704 smsg.vlan_id = curr->vlan_id;
706 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
707 do_error(LOG_ERR,"do_list_all() couldn't send VC info: %s",strerror(errno));
712 if(smsg.msgtype == OK)
713 smsg.msgtype = LIST_END;
715 smsg.msgtype = GROUP_NOT_FOUND;
717 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
718 do_error(LOG_ERR,"do_list_all() couldn't send LIST_END message: %s",strerror(errno));
722 int pvc2684d_main (int argc, char **argv)
724 int main (int argc, char **argv)
727 int test_fd, listen_fd, cli_fd;
728 socklen_t cliaddr_len;
729 struct sockaddr_un test_addr, listen_addr, cli_addr;
732 struct ucred cli_cred;
733 int ucred_len = sizeof(cli_cred);
734 struct be_msg smsg, rmsg;
735 struct stat listen_stat;
737 openlog("pvc2684d",LOG_PERROR, LOG_DAEMON);
739 bzero(group_head.name,MAX_GROUPNAME_LEN);
740 group_head.head = NULL;
741 group_head.next = NULL;
743 bzero(&test_addr, sizeof(test_addr));
744 test_addr.sun_family = AF_LOCAL;
745 strncpy(test_addr.sun_path, BRPVC_SOCKPATH, sizeof(test_addr.sun_path) -1);
746 memcpy(&listen_addr, &test_addr, sizeof(test_addr));
748 if( (test_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
749 do_error(LOG_ERR,"Couldn't create initial socket: %s",strerror(errno));
751 /* Check for already running daemon */
753 if(connect(test_fd, (struct sockaddr *) &test_addr, sizeof(test_addr))) {
754 if(errno == ECONNREFUSED)
755 unlink(BRPVC_SOCKPATH);
759 if( (listen_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
760 do_error(LOG_ERR,"Couldn't create server socket: %s",strerror(errno));
762 if( bind(listen_fd, (struct sockaddr *) &listen_addr, SUN_LEN(&listen_addr)) ) {
763 do_error(LOG_WARNING,"Another b2684d is running");
768 if(stat(BRPVC_SOCKPATH, &listen_stat))
769 do_error(LOG_ERR,"Can't fstat listen socket: %s",strerror(errno));
771 if(chmod(BRPVC_SOCKPATH, listen_stat.st_mode | S_IWOTH))
772 do_error(LOG_ERR,"Can't fchmod listen socket: %s",strerror(errno));
774 if( listen(listen_fd, 5) )
775 do_error(LOG_ERR,"listen() on server socket failed: %s",strerror(errno));
778 cliaddr_len = sizeof(cli_addr);
779 if((cli_fd = accept(listen_fd, (struct sockaddr *) &cli_addr, &cliaddr_len)) < 0) {
783 do_error(LOG_ERR,"accept() on server socket failed: %s",strerror(errno));
785 if( setsockopt(cli_fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0 )
786 do_error(LOG_ERR,"setsockopt() on client socket failed: %s",strerror(errno));
788 while((nbytes = recv(cli_fd, &rmsg, sizeof(rmsg), 0)) > 0) {
789 switch (rmsg.msgtype) {
793 if ( getsockopt(cli_fd, SOL_SOCKET, SO_PEERCRED, &cli_cred, &ucred_len) < 0 )
794 do_error(LOG_ERR,"getsockopt() for credentials failed: %s",strerror(errno));
797 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
798 do_error(LOG_ERR,"Couldn't send OK message to new client: %s",strerror(errno));
804 do_add(cli_fd,&rmsg,&cli_cred);
809 do_delete(cli_fd,&rmsg,&cli_cred);
814 do_delete_group(cli_fd,rmsg.name,&cli_cred);
819 do_list_group(cli_fd,&rmsg);
824 do_list_all(cli_fd,&rmsg);
829 if( send(cli_fd, &memstat, sizeof(memstat), 0) < 0 )
830 do_error(LOG_ERR,"Couldn't send MEM_STAT message: %s",strerror(errno));
834 smsg.msgtype = UNKNOWN_CMD;
835 if( send(cli_fd, &smsg, sizeof(smsg), 0) < 0 )
836 do_error(LOG_ERR,"Couldn't send UNKNOWN_COMMAND message: %s",strerror(errno));