文章目录
1. 使用简介
UTS(UNIX Time Sharing) namespace 是最简单的一种 namespace。UTS 中主要包含了主机名(hostname)、域名(domainname)和一些版本信息:
struct uts_namespace {
struct kref kref;
struct new_utsname name; // UTS 主要存储的信息
struct user_namespace *user_ns;
struct ucounts *ucounts;
struct ns_common ns;
} __randomize_layout;
↓
struct new_utsname {
char sysname[__NEW_UTS_LEN + 1];
char nodename[__NEW_UTS_LEN + 1]; // host name
char release[__NEW_UTS_LEN + 1];
char version[__NEW_UTS_LEN + 1];
char machine[__NEW_UTS_LEN + 1];
char domainname[__NEW_UTS_LEN + 1]; // domain name
};
其中主机名(hostname)、域名(domainname)是可以被修改的,其他只能被读取。UTS 的主要作用就是给用户态、内核态提供这些信息。
1.1 hostname
针对主机名(hostname),系统提供了 hostname
命令来进行读取和设置。下面举例说明其使用方法:
1、查看普通进程的 hostname :
pwl@ubuntu:~$ hostname
ubuntu // 当前 hostname 为 ubuntu
2、创建一个新的 UTS namespace,并设置新的 hostname :
pwl@ubuntu:~$ sudo unshare --uts /bin/bash
[sudo] password for pwl:
root@ubuntu:~# hostname
ubuntu
root@ubuntu:~# hostname test
root@ubuntu:~# hostname
test // 在新的 UTS namespace 中更改 hostname 为 test
pwl@ubuntu:~$ hostname
ubuntu // 在旧的 UTS namespace 中的 hostname 仍然为 ubuntu
1.2 domainname
针对域名(domainname),系统提供了 domainname
命令来进行读取和设置。下面举例说明其使用方法:
1、查看普通进程的 hostname :
pwl@ubuntu:~$ domainname
(none) // 当前 hostname 为空
2、创建一个新的 UTS namespace,并设置新的 hostname :
pwl@ubuntu:~$ domainname
(none)
pwl@ubuntu:~$ sudo unshare --uts /bin/bash
root@ubuntu:~# domainname
(none)
root@ubuntu:~# domainname test
root@ubuntu:~# domainname
test // 在新的 UTS namespace 中更改 hostname 为 test
root@ubuntu:~#
pwl@ubuntu:~$ domainname
(none) // 在旧的 UTS namespace 中的 hostname 仍然为空
1.3 uname
针对 UTS 提供的其他信息,系统提供了 uname
命令来进行读取,且不支持配置。
pwl@ubuntu:~$ uname -a
Linux ubuntu 4.15.0-123-generic #126-Ubuntu SMP Wed Oct 21 09:40:11 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
pwl@ubuntu:~$ uname --help
Usage: uname [OPTION]...
Print certain system information. With no OPTION, same as -s.
-a, --all print all information, in the following order,
except omit -p and -i if unknown:
-s, --kernel-name print the kernel name
-n, --nodename print the network node hostname
-r, --kernel-release print the kernel release
-v, --kernel-version print the kernel version
-m, --machine print the machine hardware name
-p, --processor print the processor type (non-portable)
-i, --hardware-platform print the hardware platform (non-portable)
-o, --operating-system print the operating system
--help display this help and exit
--version output version information and exit
2. 代码分析
我们简单分析一下 UTS namespace 的创建代码,以及几个相关系统调用的代码。
2.1 copy_utsname()
在进程创建或者 unshare()/setns() 系统调用时,如果设置了 CLONE_NEWUTS
标志会调用 copy_utsname() 创建一个新的 UTS namespace。其中的核心是创建一个新的 struct uts_namespace
结构,首先把旧的 struct uts_namespace
树复制过来:
struct uts_namespace *copy_utsname(unsigned long flags,
struct user_namespace *user_ns, struct uts_namespace *old_ns)
{
struct uts_namespace *new_ns;
BUG_ON(!old_ns);
get_uts_ns(old_ns);
if (!(flags & CLONE_NEWUTS))
return old_ns;
new_ns = clone_uts_ns(user_ns, old_ns);
put_uts_ns(old_ns);
return new_ns;
}
↓
static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
struct uts_namespace *old_ns)
{
struct uts_namespace *ns;
struct ucounts *ucounts;
int err;
err = -ENOSPC;
ucounts = inc_uts_namespaces(user_ns);
if (!ucounts)
goto fail;
err = -ENOMEM;
/* (1) 创建一个新的 uts namespace 结构 */
ns = create_uts_ns();
if (!ns)
goto fail_dec;
/* (2) 分配一个新的 namespace 编号 */
err = ns_alloc_inum(&ns->ns);
if (err)
goto fail_free;
ns->ucounts = ucounts;
ns->ns.ops = &utsns_operations;
down_read(&uts_sem);
/* (3) 拷贝旧的 uts namespace 的内容 */
memcpy(&ns->name, &old_ns->name, sizeof(ns->name));
ns->user_ns = get_user_ns(user_ns);
up_read(&uts_sem);
return ns;
fail_free:
kfree(ns);
fail_dec:
dec_uts_namespaces(ucounts);
fail:
return ERR_PTR(err);
}
后面使用 sethostname()/setdomainname() 系统调用来独立的设置各个 uts namespace 下的 hostname/domainname。
2.2 sethostname()
SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
{
int errno;
char tmp[__NEW_UTS_LEN];
if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
return -EPERM;
if (len < 0 || len > __NEW_UTS_LEN)
return -EINVAL;
errno = -EFAULT;
if (!copy_from_user(tmp, name, len)) {
struct new_utsname *u;
down_write(&uts_sem);
u = utsname();
/* (1) 设置当前 uts namespace 中的 uts_ns->name->nodename */
memcpy(u->nodename, tmp, len);
memset(u->nodename + len, 0, sizeof(u->nodename) - len);
errno = 0;
uts_proc_notify(UTS_PROC_HOSTNAME);
up_write(&uts_sem);
}
return errno;
}
↓
static inline struct new_utsname *utsname(void)
{
return ¤t->nsproxy->uts_ns->name;
}
2.3 gethostname()
SYSCALL_DEFINE2(gethostname, char __user *, name, int, len)
{
int i;
struct new_utsname *u;
char tmp[__NEW_UTS_LEN + 1];
if (len < 0)
return -EINVAL;
down_read(&uts_sem);
u = utsname();
i = 1 + strlen(u->nodename);
if (i > len)
i = len;
/* (1) 获取当前 uts namespace 中的 uts_ns->name->nodename */
memcpy(tmp, u->nodename, i);
up_read(&uts_sem);
if (copy_to_user(name, tmp, i))
return -EFAULT;
return 0;
}
2.4 setdomainname()
SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
{
int errno;
char tmp[__NEW_UTS_LEN];
if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
return -EPERM;
if (len < 0 || len > __NEW_UTS_LEN)
return -EINVAL;
errno = -EFAULT;
if (!copy_from_user(tmp, name, len)) {
struct new_utsname *u;
down_write(&uts_sem);
u = utsname();
/* (1) 设置当前 uts namespace 中的 uts_ns->name->domainname */
memcpy(u->domainname, tmp, len);
memset(u->domainname + len, 0, sizeof(u->domainname) - len);
errno = 0;
uts_proc_notify(UTS_PROC_DOMAINNAME);
up_write(&uts_sem);
}
return errno;
}
2.5 uname()
SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
{
struct new_utsname tmp;
down_read(&uts_sem);
/* (1) 获取当前 uts namespace 中的所有信息 */
memcpy(&tmp, utsname(), sizeof(tmp));
up_read(&uts_sem);
if (copy_to_user(name, &tmp, sizeof(tmp)))
return -EFAULT;
if (override_release(name->release, sizeof(name->release)))
return -EFAULT;
if (override_architecture(name))
return -EFAULT;
return 0;
}
参考文档:
1.UTS namespace详解
2.Linux内核命名空间之(4)uts namespace