[CRIU] [PATCH 4/4] sockets: Restore unconnected dgram sockets v4
Cyrill Gorcunov
gorcunov at openvz.org
Mon Apr 16 12:36:34 EDT 2012
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 restore time.
In particular this is needed for programs which do
use dgram socket to send messages to /dev/log.
Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
cr-dump.c | 6 +++
crtools.c | 9 ++++-
include/crtools.h | 2 +
include/image.h | 3 ++
include/sockets.h | 1 +
sockets.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++---
6 files changed, 104 insertions(+), 8 deletions(-)
diff --git a/cr-dump.c b/cr-dump.c
index 8c83fe8..9efcccf 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -1918,6 +1918,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:
diff --git a/crtools.c b/crtools.c
index 0c4bd0e..5c62019 100644
--- a/crtools.c
+++ b/crtools.c
@@ -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
@@ -336,7 +336,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);
@@ -359,6 +359,7 @@ int main(int argc, char *argv[])
{ "images-dir", required_argument, 0, 'D' },
{ "log-file", required_argument, 0, 'o' },
{ "namespaces", required_argument, 0, 'n' },
+ { "extern", no_argument, 0, 'x' },
{ "help", no_argument, 0, 'h' },
{ },
};
@@ -371,6 +372,9 @@ int main(int argc, char *argv[])
case 's':
opts.final_state = TASK_STOPPED;
break;
+ case 'x':
+ opts.allow_external_unix_connections = true;
+ break;
case 'p':
pid = atoi(optarg);
opts.leader_only = true;
@@ -495,6 +499,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|--extern 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");
diff --git a/include/crtools.h b/include/crtools.h
index d79878f..77bd8cf 100644
--- a/include/crtools.h
+++ b/include/crtools.h
@@ -67,6 +67,7 @@ struct cr_options {
bool leader_only;
bool show_pages_content;
bool restore_detach;
+ bool allow_external_unix_connections;
unsigned int namespaces_flags;
};
@@ -160,6 +161,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);
diff --git a/include/image.h b/include/image.h
index 9609d4e..59184b9 100644
--- a/include/image.h
+++ b/include/image.h
@@ -143,6 +143,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;
@@ -150,6 +152,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;
diff --git a/include/sockets.h b/include/sockets.h
index 2e9a915..9d78370 100644
--- a/include/sockets.h
+++ b/include/sockets.h
@@ -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);
diff --git a/sockets.c b/sockets.c
index 27a9732..9d2f9c8 100644
--- a/sockets.c
+++ b/sockets.c
@@ -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 %x with unreachable peer %x (%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,56 @@ err:
return -1;
}
+int dump_external_sockets(void)
+{
+ struct socket_desc *head, *sd;
+ int i, ret = -1;
+
+ 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 = -1u;
+
+ 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 +1191,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));
@@ -1358,6 +1418,14 @@ static int open_unixsk_standalone(struct unix_sk_info *ui)
{
int sk;
+ /*
+ * If this entry for external socket name
+ * reference only, there is nothing we should
+ * do over.
+ */
+ if (ui->ue.uflags & USK_EXTERN)
+ return -1;
+
pr_info("Opening standalone socket (id 0x%x peer 0x%x)\n",
ui->ue.id, ui->ue.peer);
@@ -1373,6 +1441,13 @@ static int open_unixsk_standalone(struct unix_sk_info *ui)
if (bind_unix_sk(sk, ui))
return -1;
+ /*
+ * No connect should be done over external peers,
+ * bind only.
+ */
+ if (ui->peer && ui->peer->ue.uflags & USK_EXTERN)
+ return sk;
+
if (ui->ue.state == TCP_LISTEN) {
pr_info("\tPutting 0x%x into listen state\n", ui->ue.id);
if (listen(sk, ui->ue.backlog) < 0) {
@@ -1483,6 +1558,10 @@ int resolve_unix_peers(void)
if (!ui->ue.peer)
continue;
+ if ((ui->ue.uflags & USK_EXTERN) &&
+ opts.allow_external_unix_connections)
+ continue;
+
peer = find_unix_sk(ui->ue.peer);
if (!peer) {
pr_err("FATAL: Peer 0x%x unresolved for 0x%x\n",
--
1.7.7.6
More information about the CRIU
mailing list