Commit 19c1de82 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Pavel Emelyanov

sockets: Restore unconnected dgram sockets v7

In case if dgram socket peer is not connected back
we can try to resolve peer by name.

For security reason this happens only if '-x' option
is passed at checkpoint and restore time.

In particular this is needed for programs which do
use dgram socket to send messages to /dev/log.
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent fd85b194
......@@ -1905,6 +1905,12 @@ int cr_dump_tasks(pid_t pid, const struct cr_options *opts)
}
ret = cr_dump_shmem();
if (ret)
goto err;
ret = dump_external_sockets();
if (ret)
goto err;
fd_id_show_tree();
err:
......
......@@ -23,7 +23,7 @@
#include "uts_ns.h"
#include "ipc_ns.h"
static struct cr_options opts;
struct cr_options opts;
/*
* The cr fd set is the set of files where the information
......@@ -322,7 +322,7 @@ int main(int argc, char *argv[])
int log_inited = 0;
int log_level = 0;
static const char short_opts[] = "dsf:p:t:hcD:o:n:v";
static const char short_opts[] = "dsf:p:t:hcD:o:n:vx";
BUILD_BUG_ON(PAGE_SIZE != PAGE_IMAGE_SIZE);
......@@ -345,6 +345,7 @@ int main(int argc, char *argv[])
{ "images-dir", required_argument, 0, 'D' },
{ "log-file", required_argument, 0, 'o' },
{ "namespaces", required_argument, 0, 'n' },
{ "ext-unix-sk", no_argument, 0, 'x' },
{ "help", no_argument, 0, 'h' },
{ },
};
......@@ -357,6 +358,9 @@ int main(int argc, char *argv[])
case 's':
opts.final_state = TASK_STOPPED;
break;
case 'x':
opts.ext_unix_sk = true;
break;
case 'p':
pid = atoi(optarg);
opts.leader_only = true;
......@@ -481,6 +485,7 @@ usage:
pr_msg(" -s|--leave-stopped leave tasks in stopped state after checkpoint instead of killing them\n");
pr_msg(" -n|--namespaces checkpoint/restore namespaces - values must be separated by comma\n");
pr_msg(" supported: uts, ipc\n");
pr_msg(" -x|--ext-unix-sk allow external unix connections\n");
pr_msg("\nAdditional common parameters:\n");
pr_msg(" -D|--images-dir dir specifis directory where checkpoint files are/to be located\n");
......
......@@ -65,6 +65,7 @@ struct cr_options {
bool leader_only;
bool show_pages_content;
bool restore_detach;
bool ext_unix_sk;
unsigned int namespaces_flags;
};
......@@ -154,6 +155,7 @@ static inline int fdset_fd(const struct cr_fdset *fdset, int type)
}
extern struct cr_fdset *glob_fdset;
extern struct cr_options opts;
int cr_dump_tasks(pid_t pid, const struct cr_options *opts);
int cr_restore_tasks(pid_t pid, struct cr_options *opts);
......
......@@ -124,6 +124,8 @@ struct pipe_data_entry {
*/
#define PIPE_NONALIG_DATA (15 * PAGE_SIZE)
#define USK_EXTERN (1 << 0)
struct unix_sk_entry {
u32 id;
u8 type;
......@@ -131,6 +133,7 @@ struct unix_sk_entry {
u8 namelen; /* fits UNIX_PATH_MAX */
u8 pad;
u32 flags;
u32 uflags; /* own service flags */
u32 backlog;
u32 peer;
fown_t fown;
......
......@@ -14,6 +14,7 @@ struct fdinfo_list_entry;
struct file_desc;
struct fdinfo_entry;
extern int collect_sockets(void);
extern int dump_external_sockets(void);
extern int collect_inet_sockets(void);
extern int collect_unix_sockets(void);
extern int resolve_unix_peers(void);
......
......@@ -43,6 +43,7 @@ struct socket_desc {
unsigned int ino;
struct socket_desc *next;
int already_dumped;
bool external;
};
struct unix_sk_desc {
......@@ -397,6 +398,7 @@ static int dump_one_unix(const struct socket_desc *_sk, struct fd_parms *p,
ue.backlog = sk->wqlen;
ue.peer = sk->peer_ino;
ue.fown = p->fown;
ue.uflags = 0;
if (ue.peer) {
struct unix_sk_desc *peer;
......@@ -412,10 +414,18 @@ static int dump_one_unix(const struct socket_desc *_sk, struct fd_parms *p,
* Peer should have us as peer or have a name by which
* we can access one.
*/
if (!peer->name && (peer->peer_ino != ue.id)) {
pr_err("Unix socket 0x%x with unreachable peer 0x%x (0x%x/%s)\n",
ue.id, ue.peer, peer->peer_ino, peer->name);
goto err;
if (peer->peer_ino != ue.id) {
if (!peer->name) {
pr_err("Unix socket 0x%x with unreachable peer 0x%x (0x%x/%s)\n",
ue.id, ue.peer, peer->peer_ino, peer->name);
goto err;
}
/*
* It can be external socket, so we defer dumping
* until all sockets the program owns are processed.
*/
peer->sd.external = true;
}
} else if (ue.state == TCP_ESTABLISHED) {
const struct unix_sk_listen_icon *e;
......@@ -733,6 +743,61 @@ err:
return -1;
}
int dump_external_sockets(void)
{
struct socket_desc *head, *sd;
int i, ret = -1;
if (!opts.ext_unix_sk)
return 0;
pr_debug("Dumping external sockets\n");
for (i = 0; i < SK_HASH_SIZE; i++) {
head = sockets[i];
if (!head)
continue;
for (sd = head; sd; sd = sd->next) {
struct unix_sk_entry e = { };
struct unix_sk_desc *sk;
if (sd->already_dumped ||
sd->external == false ||
sd->family != AF_UNIX)
continue;
sk = container_of(sd, struct unix_sk_desc, sd);
if (sk->type != SOCK_DGRAM)
continue;
e.id = sd->ino;
e.type = SOCK_DGRAM;
e.state = TCP_LISTEN;
e.namelen = sk->namelen;
e.uflags = USK_EXTERN;
e.peer = 0;
show_one_unix("Dumping extern", sk);
if (write_img(fdset_fd(glob_fdset, CR_FD_UNIXSK), &e))
goto err;
if (write_img_buf(fdset_fd(glob_fdset, CR_FD_UNIXSK),
sk->name, e.namelen))
goto err;
show_one_unix_img("Dumped extern", &e);
sd->already_dumped = 1;
}
}
return 0;
err:
return -1;
}
int collect_sockets(void)
{
int err = 0, tmp;
......@@ -1131,9 +1196,9 @@ void show_unixsk(int fd, struct cr_options *o)
if (ret <= 0)
goto out;
pr_msg("id 0x%8x type %s state %s namelen %4d backlog %4d peer 0x%8x flags 0x%2x",
pr_msg("id 0x%8x type %s state %s namelen %4d backlog %4d peer 0x%8x flags 0x%2x uflags 0x%2x",
ue.id, sktype2s(ue.type), skstate2s(ue.state),
ue.namelen, ue.backlog, ue.peer, ue.flags);
ue.namelen, ue.backlog, ue.peer, ue.flags, ue.uflags);
if (ue.namelen) {
BUG_ON(ue.namelen > sizeof(buf));
......@@ -1454,7 +1519,8 @@ int collect_unix_sockets(void)
* Make FS clean from sockets we're about to
* restore. See for how we bind them for details
*/
if (ui->name[0] != '\0')
if (ui->name[0] != '\0' &&
!(ui->ue.uflags & USK_EXTERN))
unlink(ui->name);
} else
ui->name = NULL;
......@@ -1484,6 +1550,16 @@ int resolve_unix_peers(void)
continue;
peer = find_unix_sk(ui->ue.peer);
/*
* Connect to external sockets requires
* special option to be passed.
*/
if (peer &&
(peer->ue.uflags & USK_EXTERN) &&
!(opts.ext_unix_sk))
peer = NULL;
if (!peer) {
pr_err("FATAL: Peer 0x%x unresolved for 0x%x\n",
ui->ue.peer, ui->ue.id);
......
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