diff options
author | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2004-02-24 21:27:16 +0000 |
---|---|---|
committer | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2004-02-24 21:27:16 +0000 |
commit | 89db87c9d3523bcfd15441319322b6995a2bd02a (patch) | |
tree | 5ad5cad590bef104aa467e13d3758947336fec8c | |
parent | 90ee5c10094b0bcec991c1944dd2d142f9682e4f (diff) |
Add IAX2 firmware upgrade support
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@2234 f38db490-d61c-443f-a65b-d21fe96a405b
-rwxr-xr-x | Makefile | 2 | ||||
-rwxr-xr-x | channels/chan_iax2.c | 282 | ||||
-rwxr-xr-x | channels/iax2-parser.c | 38 | ||||
-rwxr-xr-x | channels/iax2-parser.h | 6 | ||||
-rwxr-xr-x | channels/iax2.h | 17 |
5 files changed, 340 insertions, 5 deletions
@@ -328,6 +328,8 @@ bininstall: all mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/sounds mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-csv mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/keys + mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/firmware + mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/firmware/iax install -m 644 keys/iaxtel.pub $(DESTDIR)$(ASTVARLIBDIR)/keys ( cd $(DESTDIR)$(ASTVARLIBDIR)/sounds ; ln -s $(ASTSPOOLDIR)/vm . ) ( cd $(DESTDIR)$(ASTVARLIBDIR)/sounds ; ln -s $(ASTSPOOLDIR)/voicemail . ) diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 35d43ef95..300ca765c 100755 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -33,7 +33,9 @@ #include <asterisk/app.h> #include <asterisk/astdb.h> #include <asterisk/musiconhold.h> +#include <sys/mman.h> #include <arpa/inet.h> +#include <dirent.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/in_systm.h> @@ -58,6 +60,7 @@ #endif #include "iax2.h" #include "iax2-parser.h" +#include "../astconf.h" #ifndef IPTOS_MINCOST #define IPTOS_MINCOST 0x02 @@ -240,6 +243,15 @@ struct iax2_peer { int notransfer; }; +struct iax_firmware { + struct iax_firmware *next; + int fd; + int mmaplen; + int dead; + struct ast_iax2_firmware_header *fwh; + unsigned char *buf; +}; + #define REG_STATE_UNREGISTERED 0 #define REG_STATE_REGSENT 1 #define REG_STATE_AUTHSENT 2 @@ -434,6 +446,11 @@ static struct ast_peer_list { ast_mutex_t lock; } peerl; +static struct ast_firmware_list { + struct iax_firmware *wares; + ast_mutex_t lock; +} waresl; + /* Extension exists */ #define CACHE_FLAG_EXISTS (1 << 0) /* Extension is non-existant */ @@ -851,6 +868,216 @@ static int iax2_queue_frame(int callno, struct ast_frame *f) return 0; } +static void destroy_firmware(struct iax_firmware *cur) +{ + /* Close firmware */ + if (cur->fwh) { + munmap(cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh))); + } + close(cur->fd); + free(cur); +} + +static int try_firmware(char *s) +{ + struct stat stbuf; + struct iax_firmware *cur; + int fd; + int res; + struct ast_iax2_firmware_header *fwh, fwh2; + struct MD5Context md5; + unsigned char sum[16]; + res = stat(s, &stbuf); + if (res < 0) { + ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno)); + return -1; + } + /* Make sure it's not a directory */ + if (S_ISDIR(stbuf.st_mode)) + return -1; + fd = open(s, O_RDONLY); + if (fd < 0) { + ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno)); + return -1; + } + if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) { + ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s); + close(fd); + return -1; + } + if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) { + ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s); + close(fd); + return -1; + } + if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) { + ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s); + close(fd); + return -1; + } + if (fwh2.devname[sizeof(fwh2.devname) - 1] || !strlen(fwh2.devname)) { + ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s); + close(fd); + return -1; + } + fwh = mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (!fwh) { + ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno)); + close(fd); + return -1; + } + MD5Init(&md5); + MD5Update(&md5, fwh->data, ntohl(fwh->datalen)); + MD5Final(sum, &md5); + if (memcmp(sum, fwh->chksum, sizeof(sum))) { + ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s); + munmap(fwh, stbuf.st_size); + close(fd); + return -1; + } + cur = waresl.wares; + while(cur) { + if (!strcmp(cur->fwh->devname, fwh->devname)) { + /* Found a candidate */ + if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version))) + /* The version we have on loaded is older, load this one instead */ + break; + /* This version is no newer than what we have. Don't worry about it. + We'll consider it a proper load anyhow though */ + munmap(fwh, stbuf.st_size); + close(fd); + return 0; + } + cur = cur->next; + } + if (!cur) { + /* Allocate a new one and link it */ + cur = malloc(sizeof(struct iax_firmware)); + if (cur) { + memset(cur, 0, sizeof(struct iax_firmware)); + cur->fd = -1; + cur->next = waresl.wares; + waresl.wares = cur; + } + } + if (cur) { + if (cur->fwh) { + munmap(cur->fwh, cur->mmaplen); + } + if (cur->fd > -1) + close(cur->fd); + cur->fwh = fwh; + cur->fd = fd; + cur->mmaplen = stbuf.st_size; + cur->dead = 0; + } + return 0; +} + +static int iax_check_version(char *dev) +{ + int res = 0; + struct iax_firmware *cur; + if (dev && strlen(dev)) { + ast_mutex_lock(&waresl.lock); + cur = waresl.wares; + while(cur) { + if (!strcmp(dev, cur->fwh->devname)) { + res = ntohs(cur->fwh->version); + break; + } + cur = cur->next; + } + ast_mutex_unlock(&waresl.lock); + } + return res; +} + +static int iax_firmware_append(struct iax_ie_data *ied, const unsigned char *dev, unsigned int desc) +{ + int res = -1; + unsigned int bs = desc & 0xff; + unsigned int start = (desc >> 8) & 0xffffff; + unsigned int bytes; + struct iax_firmware *cur; + if (dev && strlen(dev) && bs) { + start *= bs; + ast_mutex_lock(&waresl.lock); + cur = waresl.wares; + while(cur) { + if (!strcmp(dev, cur->fwh->devname)) { + iax_ie_append_int(ied, IAX_IE_FWBLOCKDESC, desc); + if (start < ntohl(cur->fwh->datalen)) { + bytes = ntohl(cur->fwh->datalen) - start; + if (bytes > bs) + bytes = bs; + iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes); + } else { + bytes = 0; + iax_ie_append(ied, IAX_IE_FWBLOCKDATA); + } + if (bytes == bs) + res = 0; + else + res = 1; + break; + } + cur = cur->next; + } + ast_mutex_unlock(&waresl.lock); + } + return res; +} + + +static void reload_firmware(void) +{ + struct iax_firmware *cur, *curl, *curp; + DIR *fwd; + struct dirent *de; + char dir[256]; + char fn[256]; + /* Mark all as dead */ + ast_mutex_lock(&waresl.lock); + cur = waresl.wares; + while(cur) { + cur->dead = 1; + cur = cur->next; + } + /* Now that we've freed them, load the new ones */ + snprintf(dir, sizeof(dir), "%s/firmware/iax", (char *)ast_config_AST_VAR_DIR); + fwd = opendir(dir); + while((de = readdir(fwd))) { + if (de->d_name[0] != '.') { + snprintf(fn, sizeof(fn), "%s/%s", dir, de->d_name); + if (!try_firmware(fn)) { + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Loaded firmware '%s'\n", de->d_name); + } + } + } + closedir(fwd); + + /* Clean up leftovers */ + cur = waresl.wares; + curp = NULL; + while(cur) { + curl = cur; + cur = cur->next; + if (curl->dead) { + if (curp) { + curp->next = cur; + } else { + waresl.wares = cur; + } + destroy_firmware(curl); + } else { + curp = cur; + } + } + ast_mutex_unlock(&waresl.lock); +} + static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final); static int __do_deliver(void *data) @@ -2614,6 +2841,27 @@ static int iax2_show_peers(int fd, int argc, char *argv[]) #undef FORMAT2 } +static int iax2_show_firmware(int fd, int argc, char *argv[]) +{ +#define FORMAT2 "%-15.15s %-15.15s %-15.15s\n" +#define FORMAT "%-15.15s %-15.4x %-15d\n" + struct iax_firmware *cur; + if ((argc != 3) && (argc != 4)) + return RESULT_SHOWUSAGE; + ast_mutex_lock(&waresl.lock); + + ast_cli(fd, FORMAT2, "Device", "Version", "Size"); + for (cur = waresl.wares;cur;cur = cur->next) { + if ((argc == 3) || (!strcasecmp(argv[3], cur->fwh->devname))) + ast_cli(fd, FORMAT, cur->fwh->devname, ntohs(cur->fwh->version), + ntohl(cur->fwh->datalen)); + } + ast_mutex_unlock(&waresl.lock); + return RESULT_SUCCESS; +#undef FORMAT +#undef FORMAT2 +} + /* JDG: callback to display iax peers in manager */ static int manager_iax2_show_peers( struct mansession *s, struct message *m ) { @@ -2742,6 +2990,10 @@ static char show_peers_usage[] = "Usage: iax2 show peers\n" " Lists all known IAX peers.\n"; +static char show_firmware_usage[] = +"Usage: iax2 show firmware\n" +" Lists all known IAX firmware images.\n"; + static char show_reg_usage[] = "Usage: iax2 show registry\n" " Lists all registration requests and status.\n"; @@ -2760,6 +3012,8 @@ static char debug_trunk_usage[] = static struct ast_cli_entry cli_show_users = { { "iax2", "show", "users", NULL }, iax2_show_users, "Show defined IAX users", show_users_usage }; +static struct ast_cli_entry cli_show_firmware = + { { "iax2", "show", "firmware", NULL }, iax2_show_firmware, "Show available IAX firmwares", show_firmware_usage }; static struct ast_cli_entry cli_show_channels = { { "iax2", "show", "channels", NULL }, iax2_show_channels, "Show active IAX channels", show_channels_usage }; static struct ast_cli_entry cli_show_peers = @@ -3606,13 +3860,14 @@ static void reg_source_db(struct iax2_peer *p) } } -static int update_registry(char *name, struct sockaddr_in *sin, int callno) +static int update_registry(char *name, struct sockaddr_in *sin, int callno, char *devtype) { /* Called from IAX thread only, with proper iaxsl lock */ struct iax_ie_data ied; struct iax2_peer *p; int msgcount; char data[80]; + int version; memset(&ied, 0, sizeof(ied)); for (p = peerl.peers;p;p = p->next) { if (!strcasecmp(name, p->name)) { @@ -3666,6 +3921,9 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno) if (p->hascallerid) iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, p->callerid); } + version = iax_check_version(devtype); + if (version) + iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version); if (p->temponly) free(p); return send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1); @@ -4174,7 +4432,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) f.subclass = uncompress_subclass(fh->csub); } if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == IAX_COMMAND_NEW) || (f.subclass == IAX_COMMAND_REGREQ) - || (f.subclass == IAX_COMMAND_POKE))) + || (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL))) new = NEW_ALLOW; } else { /* Don't knwo anything about it yet */ @@ -4195,7 +4453,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata) /* We can only raw hangup control frames */ if (((f.subclass != IAX_COMMAND_INVAL) && (f.subclass != IAX_COMMAND_TXCNT) && - (f.subclass != IAX_COMMAND_TXACC))|| + (f.subclass != IAX_COMMAND_TXACC) && + (f.subclass != IAX_COMMAND_FWDOWNL))|| (f.frametype != AST_FRAME_IAX)) raw_hangup(&sin, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, ntohs(mh->callno) & ~IAX_FLAG_FULL ); @@ -4791,7 +5050,7 @@ retryowner: if ((!strlen(iaxs[fr.callno]->secret) && !strlen(iaxs[fr.callno]->inkeys)) || (iaxs[fr.callno]->state & IAX_STATE_AUTHENTICATED)) { if (f.subclass == IAX_COMMAND_REGREL) memset(&sin, 0, sizeof(sin)); - if (update_registry(iaxs[fr.callno]->peer, &sin, fr.callno)) + if (update_registry(iaxs[fr.callno]->peer, &sin, fr.callno, ies.devicetype)) ast_log(LOG_WARNING, "Registry error\n"); break; } @@ -4884,6 +5143,17 @@ retryowner: case IAX_COMMAND_UNSUPPORT: ast_log(LOG_NOTICE, "Peer did not understand our iax command '%d'\n", ies.iax_unknown); break; + case IAX_COMMAND_FWDOWNL: + /* Firmware download */ + memset(&ied0, 0, sizeof(ied0)); + res = iax_firmware_append(&ied0, ies.devicetype, ies.fwdesc); + if (res < 0) + send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); + else if (res > 0) + send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1); + else + send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1); + break; default: ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr.callno, iaxs[fr.callno]->peercallno); memset(&ied0, 0, sizeof(ied0)); @@ -5782,6 +6052,7 @@ static int reload_config(void) for (peer = peerl.peers; peer; peer = peer->next) iax2_poke_peer(peer, 0); ast_mutex_unlock(&peerl.lock); + reload_firmware(); return 0; } @@ -6154,6 +6425,7 @@ static int __unload_module(void) ast_cli_unregister(&cli_show_users); ast_cli_unregister(&cli_show_channels); ast_cli_unregister(&cli_show_peers); + ast_cli_unregister(&cli_show_firmware); ast_cli_unregister(&cli_show_registry); ast_cli_unregister(&cli_debug); ast_cli_unregister(&cli_trunk_debug); @@ -6220,6 +6492,7 @@ int load_module(void) ast_cli_register(&cli_show_users); ast_cli_register(&cli_show_channels); ast_cli_register(&cli_show_peers); + ast_cli_register(&cli_show_firmware); ast_cli_register(&cli_show_registry); ast_cli_register(&cli_debug); ast_cli_register(&cli_trunk_debug); @@ -6273,6 +6546,7 @@ int load_module(void) for (peer = peerl.peers; peer; peer = peer->next) iax2_poke_peer(peer, 0); ast_mutex_unlock(&peerl.lock); + reload_firmware(); return res; } diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c index 8ffbd2c69..32d40d129 100755 --- a/channels/iax2-parser.c +++ b/channels/iax2-parser.c @@ -120,6 +120,11 @@ static struct iax2_ie { { IAX_IE_PROVISIONING, "PROVISIONING" }, { IAX_IE_AESPROVISIONING, "AES PROVISIONING" }, { IAX_IE_DATETIME, "DATE TIME", dump_int }, + { IAX_IE_DEVICETYPE, "DEVICE TYPE", dump_string }, + { IAX_IE_SERVICEIDENT, "SERVICE IDENT", dump_string }, + { IAX_IE_FIRMWAREVER, "FIRMWARE VER", dump_short }, + { IAX_IE_FWBLOCKDESC, "FW BLOCK DESC", dump_int }, + { IAX_IE_FWBLOCKDATA, "FW BLOCK DATA" }, }; const char *iax_ie2str(int ie) @@ -158,7 +163,11 @@ static void dump_ies(unsigned char *iedata, int len) snprintf(tmp, sizeof(tmp), " %-15.15s : %s\n", ies[x].name, interp); outputf(tmp); } else { - snprintf(tmp, sizeof(tmp), " %-15.15s : Present\n", ies[x].name); + if (ielen) + snprintf(interp, sizeof(interp), "%d bytes", ielen); + else + strcpy(interp, "Present"); + snprintf(tmp, sizeof(tmp), " %-15.15s : %s\n", ies[x].name, interp); outputf(tmp); } found++; @@ -223,6 +232,8 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s "UNSUPPORTED", "TRANSFER", "PROVISION", + "FWDOWNLD", + "FWDATA" }; char *cmds[] = { "(0?)", @@ -363,6 +374,7 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen) char tmp[256]; memset(ies, 0, sizeof(struct iax_ies)); ies->msgcount = -1; + ies->firmwarever = -1; while(datalen >= 2) { ie = data[0]; len = data[1]; @@ -507,6 +519,30 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen) } else ies->datetime = ntohl(*((unsigned int *)(data + 2))); break; + case IAX_IE_FIRMWAREVER: + if (len != sizeof(unsigned short)) { + snprintf(tmp, sizeof(tmp), "Expecting firmwarever to be %d bytes long but was %d\n", sizeof(unsigned short), len); + errorf(tmp); + } else + ies->firmwarever = ntohs(*((unsigned short *)(data + 2))); + break; + case IAX_IE_DEVICETYPE: + ies->devicetype = data + 2; + break; + case IAX_IE_SERVICEIDENT: + ies->serviceident = data + 2; + break; + case IAX_IE_FWBLOCKDESC: + if (len != sizeof(unsigned int)) { + snprintf(tmp, sizeof(tmp), "Expected block desc to be %d bytes long but was %d\n", sizeof(unsigned int), len); + errorf(tmp); + } else + ies->fwdesc = ntohl(*((unsigned int *)(data + 2))); + break; + case IAX_IE_FWBLOCKDATA: + ies->fwdata = data + 2; + ies->fwdatalen = len; + break; default: snprintf(tmp, sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len); errorf(tmp); diff --git a/channels/iax2-parser.h b/channels/iax2-parser.h index 0e74df3e3..0b4ac5639 100755 --- a/channels/iax2-parser.h +++ b/channels/iax2-parser.h @@ -44,6 +44,12 @@ struct iax_ies { int musiconhold; unsigned int transferid; unsigned int datetime; + char *devicetype; + char *serviceident; + int firmwarever; + unsigned int fwdesc; + unsigned char *fwdata; + unsigned char fwdatalen; }; #define DIRECTION_INGRESS 1 diff --git a/channels/iax2.h b/channels/iax2.h index c9cf280ca..8997a7107 100755 --- a/channels/iax2.h +++ b/channels/iax2.h @@ -65,6 +65,8 @@ #define IAX_COMMAND_UNSUPPORT 33 /* Unsupported message received */ #define IAX_COMMAND_TRANSFER 34 /* Request remote transfer */ #define IAX_COMMAND_PROVISION 35 /* Provision device */ +#define IAX_COMMAND_FWDOWNL 36 /* Download firmware */ +#define IAX_COMMAND_FWDATA 37 /* Firmware Data */ #define IAX_DEFAULT_REG_EXPIRE 60 /* By default require re-registration once per minute */ @@ -104,6 +106,11 @@ #define IAX_IE_PROVISIONING 29 /* Provisioning info */ #define IAX_IE_AESPROVISIONING 30 /* AES Provisioning info */ #define IAX_IE_DATETIME 31 /* Date/Time */ +#define IAX_IE_DEVICETYPE 32 /* Device Type -- string */ +#define IAX_IE_SERVICEIDENT 33 /* Service Identifier -- string */ +#define IAX_IE_FIRMWAREVER 34 /* Firmware revision -- u16 */ +#define IAX_IE_FWBLOCKDESC 35 /* Firmware block description -- u32 */ +#define IAX_IE_FWBLOCKDATA 36 /* Firmware block of data -- raw */ #define IAX_AUTH_PLAINTEXT (1 << 0) #define IAX_AUTH_MD5 (1 << 1) @@ -163,4 +170,14 @@ struct ast_iax2_meta_trunk_entry { unsigned short len; /* Length of data for this callno */ } __attribute__ ((__packed__)); +#define IAX_FIRMWARE_MAGIC 0x69617879 + +struct ast_iax2_firmware_header { + unsigned int magic; /* Magic number */ + unsigned short version; /* Software version */ + unsigned char devname[16]; /* Device */ + unsigned int datalen; /* Data length of file beyond header */ + unsigned char chksum[16]; /* Checksum of all data */ + unsigned char data[0]; +} __attribute__ ((__packed__)); #endif |