Commit c5b7cceb authored by Mike Hibler's avatar Mike Hibler

Aerformed. If the mserver receiving the request allows the caller to be a

proxy, then it will perform its checks against the hostIP provided rather
than the IP of the message sender.  For the Emulab subboss case, subboss
nodes (as determined from the DB) are allowed to explicitly specify hosts
that are under their control.  The master server on real boss will run with
proxying enabled.

Also, in a fit of madness, I added a version number to the master server
protocol.  This way, if at some distant point in the future (say next week)
I realize I screwed up the protocol, I can fix it without resorting to creative
retrofitting of a version number (see imagezip) or the more Orwellian
eradication and denial of past versions (what I am doing now). Furthermore,
using an ill-thought-out insight, I made the version number be an ASCII string
in case I decide to change to an all-text protocol at some equally distant
point in the future.
parent 9efa2cea
......@@ -35,7 +35,7 @@ endif
include $(OBJDIR)/Makeconf
all: frisbee frisbeed
all: frisbee frisbeed mfrisbeed
include $(TESTBED_SRCDIR)/GNUmakerules
......@@ -146,7 +146,7 @@ log.o: decls.h log.h
network.o: decls.h utils.h
trace.o: decls.h trace.h log.h
install: $(INSTALL_SBINDIR)/frisbeed $(INSTALL_SBINDIR)/frisbee
install: $(INSTALL_SBINDIR)/mfrisbeed $(INSTALL_SBINDIR)/frisbeed $(INSTALL_SBINDIR)/frisbee
client: frisbee
......@@ -154,7 +154,7 @@ client-install: client
$(INSTALL_PROGRAM) frisbee $(DESTDIR)$(CLIENT_BINDIR)
clean:
/bin/rm -f *.o *.a frisbee frisbeed frisbee.debug frisbeed.debug
/bin/rm -f *.o *.a frisbee frisbeed mfrisbeed *.debug
/bin/rm -f frisbee.tar frisbee.tar.gz
/bin/rm -rf frisbee-dist
......
......@@ -253,8 +253,8 @@ main(int argc, char **argv)
break;
case 'S':
if (!inet_aton(optarg, &serverip)) {
fprintf(stderr, "Invalid server IP `%s'\n",
if (!GetIP(optarg, &serverip)) {
fprintf(stderr, "Invalid server name '%s'\n",
optarg);
exit(1);
}
......@@ -373,13 +373,13 @@ main(int argc, char **argv)
while (1) {
if (!ClientNetFindServer(ntohl(serverip.s_addr),
portnum, imageid, method,
portnum, 0, imageid, method,
askonly, timo, &reply, NULL))
fatal("Could not get download info for '%s'",
imageid);
if (askonly) {
PrintGetInfo(imageid, &reply);
PrintGetInfo(imageid, &reply, 1);
exit(0);
}
......
......@@ -73,13 +73,26 @@ config_read(void)
return myconfig->config_read();
}
/*
* Return the set of images that IP address 'auth' can access for GET or PUT.
*
* If 'imageid' is not NULL, then we are requesting information for a specific
* image, and the 'get' and 'put' lists will return at most one entry depending
* on whether the node can GET/PUT that image.
*
* If 'reqip' is not equal to 'authip', then the former is requesting info
* on behalf of the latter. The requester must have "proxy" capability for
* this to work.
*/
int
config_get_host_authinfo(struct in_addr *in, char *imageid,
struct config_host_authinfo **get,
struct config_host_authinfo **put)
config_get_host_authinfo(struct in_addr *reqip,
struct in_addr *authip, char *imageid,
struct config_host_authinfo **get,
struct config_host_authinfo **put)
{
assert(myconfig != NULL);
return myconfig->config_get_host_authinfo(in, imageid, get, put);
return myconfig->config_get_host_authinfo(reqip, authip, imageid,
get, put);
}
void
......@@ -116,12 +129,12 @@ config_free_host_authinfo(struct config_host_authinfo *ai)
* on success, an error code otherwise.
*/
int
config_auth_by_IP(struct in_addr *host, char *imageid,
config_auth_by_IP(struct in_addr *reqip, struct in_addr *hostip, char *imageid,
struct config_host_authinfo **aip)
{
struct config_host_authinfo *ai;
if (config_get_host_authinfo(host, imageid, &ai, 0))
if (config_get_host_authinfo(reqip, hostip, imageid, &ai, 0))
return MS_ERROR_FAILED;
if (ai->hostid == NULL) {
config_free_host_authinfo(ai);
......
......@@ -518,6 +518,42 @@ allow_stddirs(char *imageid,
free(fpath);
}
/*
* Get the Emulab nodeid for the node with the indicated control net IP.
* Return a malloc'ed string on success, NULL otherwise.
*/
static char *
emulab_nodeid(struct in_addr *cnetip)
{
MYSQL_RES *res;
MYSQL_ROW row;
char *node;
res = mydb_query("SELECT node_id FROM interfaces"
" WHERE IP='%s' AND role='ctrl'",
1, inet_ntoa(*cnetip));
if (res == NULL)
return NULL;
/* No such node */
if (mysql_num_rows(res) == 0) {
mysql_free_result(res);
return NULL;
}
/* NULL node_id!? */
row = mysql_fetch_row(res);
if (!row[0]) {
error("config_host_authinfo: null node_id!?");
mysql_free_result(res);
return NULL;
}
node = mystrdup(row[0]);
mysql_free_result(res);
return node;
}
/*
* Find all images (imageid==NULL) or a specific image (imageid!=NULL)
* that a particular node can access for GET/PUT. At any time, a node is
......@@ -540,17 +576,21 @@ allow_stddirs(char *imageid,
* Return zero on success, non-zero otherwise.
*/
static int
emulab_get_host_authinfo(struct in_addr *in, char *imageid,
struct config_host_authinfo **getp,
struct config_host_authinfo **putp)
emulab_get_host_authinfo(struct in_addr *req, struct in_addr *host,
char *imageid,
struct config_host_authinfo **getp,
struct config_host_authinfo **putp)
{
MYSQL_RES *res;
MYSQL_ROW row;
char *node;
char *node, *proxy, *role = NULL;
int i, nrows;
char *wantpid = NULL, *wantname = NULL;
struct config_host_authinfo *get = NULL, *put = NULL;
if (getp == NULL && putp == NULL)
return 0;
if (getp) {
get = mymalloc(sizeof *get);
memset(get, 0, sizeof *get);
......@@ -559,34 +599,110 @@ emulab_get_host_authinfo(struct in_addr *in, char *imageid,
put = mymalloc(sizeof *put);
memset(put, 0, sizeof(*put));
}
/* Find the node name from its control net IP */
res = mydb_query("SELECT node_id"
" FROM interfaces"
" WHERE IP='%s' AND role='ctrl'",
1, inet_ntoa(*in));
assert(res != NULL);
/* No such node */
if (mysql_num_rows(res) == 0) {
/*
* If the requester is not the same as the host, then it is a proxy
* request. In Emulab, the only proxy scenarios we support are
* elabinelab inner bosses and subbosses. So we first ensure that
* the requester is listed as one of those.
*/
if (req->s_addr != host->s_addr) {
proxy = emulab_nodeid(req);
if (proxy == NULL) {
emulab_free_host_authinfo(get);
emulab_free_host_authinfo(put);
return 1;
}
/* Make sure the node really is a inner-boss/subboss */
res = mydb_query("SELECT erole,inner_elab_role FROM reserved"
" WHERE node_id='%s'", 2, proxy);
assert(res != NULL);
/* Node is free */
if (mysql_num_rows(res) == 0) {
mysql_free_result(res);
free(proxy);
emulab_free_host_authinfo(get);
emulab_free_host_authinfo(put);
return 1;
}
/*
* Is node an inner boss?
* Note that string could be "boss" or "boss+router"
* so we just check the prefix.
*/
row = mysql_fetch_row(res);
if (row[1] && strncmp(row[1], "boss", 4) == 0)
role = "innerboss";
/* or a subboss? */
else if (row[0] && strcmp(row[0], "subboss") == 0)
role = "subboss";
/* neither, return an error */
else {
mysql_free_result(res);
free(proxy);
emulab_free_host_authinfo(get);
emulab_free_host_authinfo(put);
return 1;
}
mysql_free_result(res);
} else
proxy = NULL;
/*
* Find the node name from its control net IP.
* If the node doesn't exist, we return an empty list.
*/
node = emulab_nodeid(host);
if (node == NULL) {
if (proxy) free(proxy);
if (getp) *getp = get;
if (putp) *putp = put;
return 0;
}
row = mysql_fetch_row(res);
if (!row[0]) {
error("config_host_authinfo: null node_id!?");
mysql_free_result(res);
emulab_free_host_authinfo(get);
emulab_free_host_authinfo(put);
return 1;
}
node = mystrdup(row[0]);
if (get != NULL)
get->hostid = mystrdup(node);
if (put != NULL)
put->hostid = mystrdup(node);
mysql_free_result(res);
/*
* We have a proxy node. It should be either:
* - a subboss and a frisbee subboss for the node
* - an inner-boss and in the same experiment as the node
* Note that we could not do this check until we had the node name.
* Note also that we no longer care about proxy or not after this.
*/
if (proxy) {
if (strcmp(role, "subboss") == 0)
res = mydb_query("SELECT node_id"
" FROM subbosses"
" WHERE subboss_id='%s'"
" AND node_id='%s'"
" AND service='frisbee'",
1, proxy, node);
else
res = mydb_query("SELECT r1.node_id"
" FROM reserved as r1,reserved as r2"
" WHERE r1.node_id='%s'"
" AND r2.node_id='%s'"
" AND r1.pid=r2.pid"
" AND r1.eid=r2.eid",
1, proxy, node);
assert(res != NULL);
if (mysql_num_rows(res) == 0) {
mysql_free_result(res);
free(proxy);
free(node);
emulab_free_host_authinfo(get);
emulab_free_host_authinfo(put);
return 1;
}
mysql_free_result(res);
free(proxy);
}
/* Find the pid/gid to which the node is currently assigned */
res = mydb_query("SELECT e.pid,e.gid,r.eid,u.uid"
......@@ -598,6 +714,7 @@ emulab_get_host_authinfo(struct in_addr *in, char *imageid,
/* Node is free */
if (mysql_num_rows(res) == 0) {
mysql_free_result(res);
free(node);
if (getp) *getp = get;
if (putp) *putp = put;
......@@ -996,7 +1113,8 @@ emulab_dump(FILE *fd)
struct in_addr in;
inet_aton(row[1], &in);
if (emulab_get_host_authinfo(&in, NULL, &get, &put)) {
if (emulab_get_host_authinfo(&in, &in, NULL,
&get, &put)) {
warning("Could not get node authinfo for %s",
row[0]);
}
......
......@@ -45,7 +45,8 @@ struct config {
int (*config_init)(void);
void (*config_deinit)(void);
int (*config_read)(void);
int (*config_get_host_authinfo)(struct in_addr *, char *,
int (*config_get_host_authinfo)(struct in_addr *,
struct in_addr *, char *,
struct config_host_authinfo **,
struct config_host_authinfo **);
void (*config_free_host_authinfo)(struct config_host_authinfo *);
......@@ -60,12 +61,13 @@ struct config {
extern int config_init(int);
extern void config_deinit(void);
extern int config_read(void);
extern int config_get_host_authinfo(struct in_addr *, char *,
extern int config_get_host_authinfo(struct in_addr *,
struct in_addr *, char *,
struct config_host_authinfo **,
struct config_host_authinfo **);
extern void config_dump_host_authinfo(struct config_host_authinfo *);
extern void config_free_host_authinfo(struct config_host_authinfo *);
extern int config_auth_by_IP(struct in_addr *, char *,
extern int config_auth_by_IP(struct in_addr *, struct in_addr *, char *,
struct config_host_authinfo **);
extern int config_get_server_address(struct config_imageinfo *, int, int,
in_addr_t *, in_port_t *, int *);
......
......@@ -330,6 +330,7 @@ typedef struct {
* These are sent via unicast TCP.
*/
typedef struct {
uint32_t hostip;
uint8_t methods;
uint8_t status;
uint16_t idlen;
......@@ -340,14 +341,17 @@ typedef struct {
uint8_t method;
uint8_t isrunning;
uint16_t error;
in_addr_t servaddr;
in_addr_t addr;
in_port_t port;
uint32_t servaddr;
uint32_t addr;
uint16_t port;
uint16_t sigtype;
uint8_t signature[MS_MAXSIGLEN];
uint32_t hisize;
uint32_t losize;
} __attribute__((__packed__)) GetReply;
typedef struct {
int8_t version[4];
int32_t type;
union {
GetRequest getrequest;
......@@ -355,6 +359,8 @@ typedef struct {
} body;
} MasterMsg_t;
#define MS_MSGVERS_1 "V01"
#define MS_MSGTYPE_GETREQUEST 1
#define MS_MSGTYPE_GETREPLY 2
#define MS_MSGTYPE_PUTREQUEST 3
......@@ -383,6 +389,7 @@ typedef struct {
/*
* Protos.
*/
int GetIP(char *str, struct in_addr *in);
int GetSockbufSize(void);
int ClientNetInit(void);
int ServerNetInit(void);
......@@ -394,8 +401,8 @@ void PacketReply(Packet_t *p);
int PacketValid(Packet_t *p, int nchunks);
void dump_network(void);
#ifdef MASTER_SERVER
int ClientNetFindServer(in_addr_t, in_port_t, char *, int, int, int,
GetReply *, struct in_addr *);
int ClientNetFindServer(in_addr_t, in_port_t, in_addr_t, char *,
int, int, int, GetReply *, struct in_addr *);
int MsgSend(int, MasterMsg_t *, size_t, int);
int MsgReceive(int, MasterMsg_t *, size_t, int);
#endif
......
This diff is collapsed.
......@@ -42,6 +42,25 @@ int broadcast = 0;
static int isclient = 0;
static int sndportnum; /* kept in network order */
/*
* Convert a string to an IPv4 address. We first try to interpret it as
* an IPv4 address. If that fails, we attempt to resolve it as a host name.
* Return non-zero on success.
*/
int
GetIP(char *str, struct in_addr *in)
{
struct hostent *he;
if (inet_aton(str, in) == 0) {
if ((he = gethostbyname(str)) == NULL)
return 0;
memcpy(in, he->h_addr, sizeof(*in));
}
return 1;
}
int
GetSockbufSize(void)
{
......@@ -585,11 +604,16 @@ MsgReceive(int msock, MasterMsg_t *msg, size_t size, int timo)
* for information about the image (without starting a server), 'timeout'
* is how long to wait for a response.
*
* If 'hostip' is not zero, then we are requesting information on behalf of
* that node. The calling node (us) must have "proxy" permission on the
* server for this to work.
*
* On success, return non-zero with 'reply' filled in with the server's
* response IN HOST ORDER. On failure returns zero.
*/
int
ClientNetFindServer(in_addr_t sip, in_port_t sport, char *imageid,
ClientNetFindServer(in_addr_t sip, in_port_t sport,
in_addr_t hostip, char *imageid,
int method, int askonly, int timeout,
GetReply *reply, struct in_addr *myip)
{
......@@ -630,7 +654,9 @@ ClientNetFindServer(in_addr_t sip, in_port_t sport, char *imageid,
}
memset(&msg, 0, sizeof msg);
strncpy(msg.version, MS_MSGVERS_1, sizeof(msg.version));
msg.type = htonl(MS_MSGTYPE_GETREQUEST);
msg.body.getrequest.hostip = htonl(hostip);
if (askonly) {
msg.body.getrequest.status = 1;
msg.body.getrequest.methods = MS_METHOD_ANY;
......@@ -656,6 +682,11 @@ ClientNetFindServer(in_addr_t sip, in_port_t sport, char *imageid,
return 0;
}
if (strncmp(msg.version, MS_MSGVERS_1, sizeof(msg.version))) {
fprintf(stderr, "Got incorrect version from master server\n");
close(msock);
return 0;
}
if (ntohl(msg.type) != MS_MSGTYPE_GETREPLY) {
fprintf(stderr, "Got incorrect reply from master server\n");
close(msock);
......@@ -672,7 +703,11 @@ ClientNetFindServer(in_addr_t sip, in_port_t sport, char *imageid,
reply->addr = ntohl(reply->addr);
reply->port = ntohs(reply->port);
reply->sigtype = ntohs(reply->sigtype);
if (reply->sigtype == MS_SIGTYPE_MTIME)
*(uint32_t *)reply->signature =
ntohl(*(uint32_t *)reply->signature);
reply->hisize = ntohl(reply->hisize);
reply->losize = ntohl(reply->losize);
return 1;
}
#endif
......
......@@ -604,6 +604,9 @@ GetMSError(int error)
char *err;
switch (error) {
case 0:
err = "no error";
break;
case MS_ERROR_FAILED:
err = "server authentication error";
break;
......@@ -660,30 +663,90 @@ GetMSMethods(int methods)
return mbuf;
}
/*
* Print the contents of a GET reply to stdout.
* The reply struct fields should be in HOST order; i.e., as returned
* by ClientNetFindServer.
*/
void
PrintGetInfo(char *imageid, GetReply *reply)
PrintGetInfo(char *imageid, GetReply *reply, int raw)
{
struct in_addr in;
uint64_t isize;
int len;
if (raw) {
printf("imageid=%s\n", imageid);
printf("error=%d\n", reply->error);
if (reply->error)
return;
printf("method=0x%x\n", reply->method);
isize = ((uint64_t)reply->hisize << 32) | reply->losize;
printf("size=%llu\n", isize);
printf("sigtype=0x%x\n", reply->sigtype);
switch (reply->sigtype) {
case MS_SIGTYPE_MTIME:
/* XXX has been converted to 32-bit host order */
printf("sig=0x%x\n", *(uint32_t *)reply->signature);
len = 0;
break;
case MS_SIGTYPE_MD5:
len = 16;
break;
case MS_SIGTYPE_SHA1:
len = 20;
break;
default:
len = 0;
break;
}
if (len > 0) {
char sigbuf[MS_MAXSIGLEN*2+1], *sbp;
int i;
sbp = sigbuf;
for (i = 0; i < len; i++) {
sprintf(sbp, "%02x", reply->signature[i]);
sbp += 2;
}
*sbp = '\0';
printf("sig=0x%s\n", sigbuf);
}
printf("running=%d\n", reply->isrunning);
if (reply->isrunning) {
in.s_addr = htonl(reply->servaddr);
printf("servaddr=%s\n", inet_ntoa(in));
in.s_addr = htonl(reply->addr);
printf("addr=%s\n", inet_ntoa(in));
printf("port=%d\n", reply->port);
}
return;
}
if (reply->error) {
log("%s: server denied access: %s",
printf("%s: server denied access: %s\n",
imageid, GetMSError(reply->error));
return;
}
if (reply->isrunning) {
struct in_addr in;
in.s_addr = htonl(reply->addr);
log("%s: access allowed, server running at %s:%d using %s",
printf("%s: access OK, server running at %s:%d using %s\n",
imageid, inet_ntoa(in), reply->port,
GetMSMethods(reply->method));
} else
log("%s: access allowed, available methods=%s",
printf("%s: access OK, available methods=%s\n",
imageid, GetMSMethods(reply->method));
isize = ((uint64_t)reply->hisize << 32) | reply->losize;
printf(" size=%llu\n", isize);
switch (reply->sigtype) {
case MS_SIGTYPE_MTIME:
{
time_t mt = ntohl(*(time_t *)reply->signature);
log(" modtime=%s", ctime(&mt));
time_t mt = *(time_t *)reply->signature;
printf(" modtime=%s\n", ctime(&mt));
}
default:
break;
......
......@@ -74,7 +74,7 @@ void ClientStatsDump(unsigned int id, ClientStats_t *stats);
#ifdef MASTER_SERVER
char *GetMSError(int error);
char *GetMSMethods(int methods);
void PrintGetInfo(char *imageid, GetReply *reply);
void PrintGetInfo(char *imageid, GetReply *reply, int raw);
#endif
/* Compat */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment