2 protocol.c -- handle the meta-protocol
3 Copyright (C) 1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>,
4 2000 Guus Sliepen <guus@sliepen.warande.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 $Id: protocol.c,v 1.28.4.34 2000/09/22 15:06:28 guus Exp $
25 #include <sys/types.h>
30 #include <sys/socket.h>
37 #include <netinet/in.h>
39 #include <openssl/sha.h>
49 int check_id(char *id)
53 for (i = 0; i < strlen(id); i++)
55 if(!isalpha(id[i]) && id[i] != '_')
64 /* Generic outgoing request routine - takes care of logging and error detection as well */
66 int send_request(conn_list_t *cl, const char *format, int request, /*args*/ ...)
69 char buffer[MAXBUFSIZE+1];
73 /* Use vsnprintf instead of vasprintf: faster, no memory fragmentation, cleanup is automatic,
74 and there is a limit on the input buffer anyway */
76 va_start(args, request);
77 len = vsnprintf(buffer, MAXBUFSIZE+1, format, args);
80 if(len < 0 || len > MAXBUFSIZE)
82 syslog(LOG_ERR, _("Output buffer overflow while sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
86 if(debug_lvl >= DEBUG_META)
87 syslog(LOG_DEBUG, _("Sending %s to %s (%s): %s"), request_name[request],
88 cl->name, cl->hostname, buffer);
89 else if(debug_lvl >= DEBUG_PROTOCOL)
90 syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
93 if(cl->status.encryptin)
95 /* FIXME: Do encryption */
98 if((write(cl->meta_socket, buffer, len)) < 0)
100 syslog(LOG_ERR, _("Sending meta data failed: %m"));
106 /* Connection protocol:
115 ---------------------------------------
116 Any negotations about the meta protocol
117 encryption go here(u).
118 ---------------------------------------
121 ---------------------------------------
127 (E) Encrypted with symmetric cipher.
129 Part of the challenge is directly used to set the symmetric cipher key and the initial vector.
130 Since a man-in-the-middle cannot decrypt the RSA challenges, this means that he cannot get or
131 forge the key for the symmetric cipher.
134 int send_id(conn_list_t *cl)
139 x = send_request(cl, "%d %s %d %s", ID, myself->name, myself->protocol_version, optstr=opt2str(myself->options));
145 int id_h(conn_list_t *cl)
150 if(sscanf(cl->buffer, "%*d %as %d %as", &cl->name, &cl->protocol_version, &optstr) != 3)
152 syslog(LOG_ERR, _("Got bad ID from %s"), cl->hostname);
156 /* Check if version matches */
158 if(cl->protocol_version != myself->protocol_version)
160 syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
161 cl->name, cl->hostname, cl->protocol_version);
165 /* Check if option string is valid */
167 if((cl->options = str2opt(optstr)) == -1)
169 syslog(LOG_ERR, _("Peer %s uses invalid option string"), cl->hostname);
176 /* Check if identity is a valid name */
178 if(!check_id(cl->name))
180 syslog(LOG_ERR, _("Peer %s uses invalid identity name"), cl->hostname);
184 /* Load information about peer */
188 syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), cl->hostname, cl->name);
193 /* First check if the host we connected to is already in our
194 connection list. If so, we are probably making a loop, which
198 if(cl->status.outgoing)
200 if((old = lookup_id(cl->name)))
202 if(debug_lvl > DEBUG_CONNECTIONS)
203 syslog(LOG_NOTICE, _("Uplink %s (%s) is already in our connection list"), cl->name, cl->hostname);
204 cl->status.outgoing = 0;
205 old->status.outgoing = 1;
206 terminate_connection(cl);
211 /* Send a challenge to verify the identity */
213 cl->allow_request = CHAL_REPLY;
215 return send_challenge(cl);
218 int send_challenge(conn_list_t *cl)
220 char buffer[CHAL_LENGTH*2+1];
222 /* Allocate buffers for the challenge */
224 if(!cl->hischallenge)
225 cl->hischallenge = xmalloc(CHAL_LENGTH);
227 /* Copy random data to the buffer */
229 RAND_bytes(cl->hischallenge, CHAL_LENGTH);
231 /* Convert the random data to a hexadecimal formatted string */
233 bin2hex(cl->hischallenge,buffer,CHAL_LENGTH);
234 buffer[keylength*2] = '\0';
236 /* Send the challenge */
238 cl->allow_request = CHAL_REPLY;
240 return send_request(cl, "%d %s", CHALLENGE, buffer);
243 int challenge_h(conn_list_t *cl)
247 if(sscanf(cl->buffer, "%*d %as", &buffer) != 1)
249 syslog(LOG_ERR, _("Got bad CHALLENGE from %s (%s)"), cl->name, cl->hostname);
253 /* Check if the length of the challenge is all right */
255 if(strlen(buffer) != CHAL_LENGTH*2)
257 syslog(LOG_ERR, _("Intruder: wrong challenge length from %s (%s)"), cl->name, cl->hostname);
262 /* Allocate buffers for the challenge */
265 cl->mychallenge = xmalloc(CHAL_LENGTH);
267 /* Convert the challenge from hexadecimal back to binary */
269 hex2bin(buffer,cl->mychallenge,CHAL_LENGTH);
272 /* Rest is done by send_chal_reply() */
274 return send_chal_reply(cl);
277 int send_chal_reply(conn_list_t *cl)
279 char hash[SHA_DIGEST_LENGTH*2+1];
283 syslog(LOG_ERR, _("Trying to send CHAL_REPLY to %s (%s) without a valid CHALLENGE"), cl->name, cl->hostname);
287 /* Calculate the hash from the challenge we received */
289 SHA1(cl->mychallenge, CHAL_LENGTH, hash);
291 /* Convert the hash to a hexadecimal formatted string */
293 bin2hex(hash,hash,SHA_DIGEST_LENGTH);
294 hash[SHA_DIGEST_LENGTH*2] = '\0';
298 if(cl->status.outgoing)
299 cl->allow_request = ID;
301 cl->allow_request = ACK;
304 return send_request(cl, "%d %s", CHAL_REPLY, hash);
307 int chal_reply_h(conn_list_t *cl)
310 char myhash[SHA_DIGEST_LENGTH];
312 if(sscanf(cl->buffer, "%*d %as", &hishash) != 2)
314 syslog(LOG_ERR, _("Got bad CHAL_REPLY from %s (%s)"), cl->name, cl->hostname);
318 /* Check if the length of the hash is all right */
320 if(strlen(hishash) != SHA_DIGEST_LENGTH*2)
322 syslog(LOG_ERR, _("Intruder: wrong challenge reply length from %s (%s)"), cl->name, cl->hostname);
326 /* Convert the hash to binary format */
328 hex2bin(hishash, hishash, SHA_DIGEST_LENGTH);
330 /* Calculate the hash from the challenge we sent */
332 SHA1(cl->hischallenge, CHAL_LENGTH, myhash);
334 /* Verify the incoming hash with the calculated hash */
336 if(!memcmp(hishash, myhash, SHA_DIGEST_LENGTH))
338 syslog(LOG_ERR, _("Intruder: wrong challenge reply from %s (%s)"), cl->name, cl->hostname);
345 /* Identity has now been positively verified.
346 If we are accepting this new connection, then send our identity,
347 if we are making this connecting, acknowledge.
350 if(cl->status.outgoing)
352 cl->allow_request = ACK;
357 cl->allow_request = CHALLENGE;
362 int send_ack(conn_list_t *cl)
365 return send_request(cl, "%d", ACK);
368 int ack_h(conn_list_t *cl)
372 /* Okay, before we active the connection, we check if there is another entry
373 in the connection list with the same name. If so, it presumably is an
374 old connection that has timed out but we don't know it yet.
377 while((old = lookup_id(cl->name)))
379 if(debug_lvl > DEBUG_CONNECTIONS)
380 syslog(LOG_NOTICE, _("Removing old entry for %s at %s in favour of new connection from %s"),
381 cl->name, old->hostname, cl->hostname);
382 old->status.active = 0;
383 terminate_connection(old);
386 /* Activate this connection */
388 cl->allow_request = ALL;
389 cl->status.active = 1;
391 if(debug_lvl > DEBUG_CONNECTIONS)
392 syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), cl->name, cl->hostname);
394 /* Exchange information about other tinc daemons */
396 notify_others(cl, NULL, send_add_host);
402 if(cl->status.outgoing)
408 /* Address and subnet information exchange */
410 int send_add_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
415 x = send_request(cl, "%d %s %s", ADD_SUBNET,
416 other->name, netstr = net2str(subnet));
422 int add_subnet_h(conn_list_t *cl)
427 subnet_t *subnet, *old;
429 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
431 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s)"), cl->name, cl->hostname);
432 free(name); free(subnetstr);
436 /* Check if owner name is a valid */
440 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
441 free(name); free(subnetstr);
445 /* Check if subnet string is valid */
447 if((subnet = str2net(subnetstr)) == -1)
449 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
450 free(name); free(subnetstr);
456 /* Check if somebody tries to add a subnet of ourself */
458 if(!strcmp(name, myself->name))
460 syslog(LOG_ERR, _("Warning: got ADD_SUBNET from %s (%s) for ourself, restarting"),
461 cl->name, cl->hostname);
467 /* Check if the owner of the new subnet is in the connection list */
469 if(!(owner = lookup_id(name))
471 syslog(LOG_ERR, _("Got ADD_SUBNET for %s from %s (%s) which is not in our connection list"),
472 name, cl->name, cl->hostname);
477 /* If everything is correct, add the subnet to the list of the owner */
479 return subnet_add(owner, subnet);
482 int send_del_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
485 return send_request(cl, "%d %s %s", DEL_SUBNET, other->name, net2str(subnet));
488 int del_subnet_h(conn_list_t *cl)
493 subnet_t *subnet, *old;
495 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
497 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
498 free(name); free(subnetstr);
502 /* Check if owner name is a valid */
506 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
507 free(name); free(subnetstr);
511 /* Check if subnet string is valid */
513 if((subnet = str2net(subnetstr)) == -1)
515 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
516 free(name); free(subnetstr);
522 /* Check if somebody tries to add a subnet of ourself */
524 if(!strcmp(name, myself->name))
526 syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
527 cl->name, cl->hostname);
533 /* Check if the owner of the new subnet is in the connection list */
535 if(!(owner = lookup_id(name))
537 syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
538 name, cl->name, cl->hostname);
543 /* If everything is correct, add the subnet to the list of the owner */
545 return subnet_del(owner, subnet);
548 /* New and closed connections notification */
550 int send_add_host(conn_list_t *cl, conn_list_t *other)
555 x = send_request(cl, "%d %s %s %lx:%d %s", ADD_HOST,
556 myself->name, other->name, other->real_ip, other->port, optstr = opt2str(other->options));
562 int add_host_h(conn_list_t *cl)
566 conn_list_t *old, *new, *hisuplink;
568 new = new_conn_list();
570 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %as", &sender, &new->name, &new->address, &new->port, &optstr) != 5)
572 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
576 /* Check if option string is valid */
578 if((new->options = str2opt(optstr)) == -1)
580 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid option string"), cl->name, cl->hostname);
581 free(optstr); free(sender);
587 /* Check if identity is a valid name */
589 if(!check_id(new->name) || !check_id(sender))
591 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
596 /* Check if somebody tries to add ourself */
598 if(!strcmp(new->name, myself->name))
600 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
606 /* We got an ADD_HOST from ourself!? */
608 if(!strcmp(sender, myself->name))
610 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
616 /* Lookup his uplink */
618 if(!(new->hisuplink = lookup_id(sender))
620 syslog(LOG_ERR, _("Got ADD_HOST from %s (%s) with origin %s which is not in our connection list"),
621 sender, cl->name, cl->hostname);
628 /* Fill in more of the new conn_list structure */
630 new->hostname = hostlookup(htonl(new->real_ip));
632 /* Check if the new host already exists in the connnection list */
634 if((old = lookup_id(new->name)))
636 if((new->real_ip == old->real_ip) && (new->port == old->port))
638 if(debug_lvl > DEBUG_CONNECTIONS)
639 syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
640 old->name, old->hostname, new->name, new->hostname);
645 if(debug_lvl > DEBUG_CONNECTIONS)
646 syslog(LOG_NOTICE, _("Removing old entry for %s (%s)"),
647 old->name, old->hostname);
648 old->status.active = 0;
649 terminate_connection(old);
653 /* Fill in rest of conn_list structure */
656 new->status.active = 1;
658 /* Hook it up into the conn_list */
660 conn_list_add(conn_list, new);
662 /* Tell the rest about the new host */
664 notify_others(new, cl, send_add_host);
670 int send_del_host(conn_list_t *cl, conn_list_t *other)
675 x = send_request(cl, "%d %s %s %lx:%d %s", DEL_HOST,
676 myself->name, other->name, other->real_ip, other->port, optstr = opt2str(other->options));
682 int del_host_h(conn_list_t *cl)
690 conn_list_t *old, *hisuplink;
693 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %as", &sender, &name, &address, &port, &optstr) != 5)
695 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
696 cl->name, cl->hostname);
700 /* Check if option string is valid */
702 if((options = str2opt(optstr)) == -1)
704 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid option string"), cl->name, cl->hostname);
705 free(optstr); free(sender); free(name);
711 /* Check if identity is a valid name */
713 if(!check_id(name) || !check_id(sender))
715 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
716 free(name); free(sender);
720 /* Check if somebody tries to delete ourself */
722 if(!strcmp(name, myself->name))
724 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
725 cl->name, cl->hostname);
726 free(name); free(sender);
731 /* We got an ADD_HOST from ourself!? */
733 if(!strcmp(sender, myself->name))
735 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
737 free(name); free(sender);
741 /* Lookup his uplink */
743 if(!(hisuplink = lookup_id(sender))
745 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) with origin %s which is not in our connection list"),
746 cl->name, cl->hostname, sender);
747 free(name); free(sender);
753 /* Check if the new host already exists in the connnection list */
755 if(!(old = lookup_id(name)))
757 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
758 name, cl->name, cl->hostname);
763 /* Check if the rest matches */
765 if(address!=old->address || port!=old->port || options!=old->options || hisuplink!=old->hisuplink || cl!=old->myuplink)
767 syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
771 /* Ok, since EVERYTHING seems to check out all right, delete it */
773 old->status.termreq = 1;
774 old->status.active = 0;
776 terminate_connection(old);
781 /* Status and error notification routines */
783 int send_status(conn_list_t *cl, int statusno, char *statusstring)
787 statusstring = status_text[statusno];
789 return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
792 int status_h(conn_list_t *cl)
797 if(sscanf(cl->buffer, "%*d %d %as", &statusno, &statusstring) != 2)
799 syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
800 cl->name, cl->hostname);
804 if(debug_lvl > DEBUG_STATUS)
806 syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
807 cl->name, cl->hostname, status_text[statusno], statusstring);
815 int send_error(conn_list_t *cl, int errno, char *errstring)
819 errstring = strerror(errno);
820 return send_request(cl, "%d %d %s", ERROR, errno, errstring);
823 int error_h(conn_list_t *cl)
828 if(sscanf(cl->buffer, "%*d %d %as", &errno, &errorstring) != 2)
830 syslog(LOG_ERR, _("Got bad error from %s (%s)"),
831 cl->name, cl->hostname);
835 if(debug_lvl > DEBUG_error)
837 syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
838 cl->name, cl->hostname, strerror(errno), errorstring);
842 cl->status.termreq = 1;
843 terminate_connection(cl);
848 int send_termreq(conn_list_t *cl)
851 return send_request(cl, "%d", TERMREQ);
854 int termreq_h(conn_list_t *cl)
857 cl->status.termreq = 1;
858 terminate_connection(cl);
863 /* Keepalive routines - FIXME: needs a closer look */
865 int send_ping(conn_list_t *cl)
867 cl->status.pinged = 1;
869 return send_request(cl, "%d", PING);
872 int ping_h(conn_list_t *cl)
875 return send_pong(cl);
878 int send_pong(conn_list_t *cl)
881 return send_request(cl, "%d", PONG);
884 int pong_h(conn_list_t *cl)
887 cl->status.got_pong = 1;
894 int send_key_changed(conn_list_t *from, conn_list_t *cl)
898 for(p = conn_list; p != NULL; p = p->next)
900 if(p!=cl && p->status.meta && p->status.active)
901 send_request(p, "%d %s", KEY_CHANGED,
908 int key_changed_h(conn_list_t *cl)
913 if(sscanf(cl->buffer, "%*d %as", &from_id) != 1)
915 syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
916 cl->name, cl->hostname);
920 if(!(from = lookup_id(from_id)))
922 syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
923 cl->name, cl->hostname, from_id);
930 from->status.validkey = 0;
931 from->status.waitingforkey = 0;
933 send_key_changed(from, cl);
938 int send_req_key(conn_list_t *from, conn_list_t *to)
941 return send_request(to->nexthop, "%d %s %s", REQ_KEY,
942 from->name, to->name);
945 int req_key_h(conn_list_t *cl)
947 char *from_id, *to_id;
948 conn_list_t *from, *to;
950 if(sscanf(cl->buffer, "%*d %as %as", &from_id, &to_id) != 2)
952 syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
953 cl->name, cl->hostname);
957 if(!(from = lookup_id(from_id)))
959 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
960 cl->name, cl->hostname, from_id);
961 free(from_id); free(to_id);
965 /* Check if this key request is for us */
967 if(!strcmp(to_id, myself->name))
969 send_ans_key(myself, from, myself->datakey->key);
973 if(!(to = lookup_id(to_id)))
975 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
976 cl->name, cl->hostname, to_id);
977 free(from_id); free(to_id);
980 send_req_key(from, to);
983 free(from_id); free(to_id);
988 int send_ans_key(conn_list_t *from, conn_list_t *to, char *datakey)
991 return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
992 from->name, to->name, datakey);
995 int ans_key_h(conn_list_t *cl)
997 char *from_id, *to_id, *datakey;
999 conn_list_t *from, *to;
1001 if(sscanf(cl->buffer, "%*d %as %as %as", &from_id, &to_id, &datakey) != 3)
1003 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
1004 cl->name, cl->hostname);
1008 if(!(from = lookup_id(from_id)))
1010 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
1011 cl->name, cl->hostname, from_id);
1012 free(from_id); free(to_id); free(datakey);
1016 /* Check if this key request is for us */
1018 if(!strcmp(to_id, myself->name))
1020 /* It is for us, convert it to binary and set the key with it. */
1022 keylength = strlen(datakey);
1024 if((keylength%2) || (keylength <= 0))
1026 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key"),
1027 cl->name, cl->hostname, from->name);
1028 free(from_id); free(to_id); free(datakey);
1032 hex2bin(datakey, datakey, keylength);
1033 BF_set_key(cl->datakey, keylength, datakey);
1037 if(!(to = lookup_id(to_id)))
1039 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
1040 cl->name, cl->hostname, to_id);
1041 free(from_id); free(to_id); free(datakey);
1044 send_ans_key(from, to, datakey);
1047 free(from_id); free(to_id); free(datakey);
1055 Notify all my direct connections of a new host
1056 that was added to the vpn, with the exception
1057 of the source of the announcement.
1060 int notify_others(conn_list_t *new, conn_list_t *source,
1061 int (*function)(conn_list_t*, conn_list_t*))
1065 for(p = conn_list; p != NULL; p = p->next)
1066 if(p != new && p != source && p->status.meta && p->status.active)
1073 Notify one connection of everything
1077 int notify_one(conn_list_t *new)
1081 for(p = conn_list; p != NULL; p = p->next)
1082 if(p != new && p->status.active)
1083 send_add_host(new, p);
1088 /* "Complete overhaul". */
1090 int (*request_handlers[])(conn_list_t*) = {
1091 id_h, challenge_h, chal_reply_h, ack_h,
1092 status_h, error_h, termreq_h,
1094 add_host_h, del_host_h,
1095 add_subnet_h, del_subnet_h,
1096 key_changed_h, req_key_h, ans_key_h,
1099 char (*request_name[]) = {
1100 "ID", "CHALLENGE", "CHAL_REPLY", "ACK",
1101 "STATUS", "ERROR", "TERMREQ",
1103 "ADD_HOST", "DEL_HOST",
1104 "ADD_SUBNET", "DEL_SUBNET",
1105 "KEY_CHANGED", "REQ_KEY", "ANS_KEY",