Skip to content

Commit d6c0d52

Browse files
committed
Support time namespace
"time" namespace was introduced in Linux v5.6 support new time namespace to set boottime and monotonic time offset Example runtime spec "timeOffsets": { "monotonic": { "secs": 172800, "nanosecs": 0 }, "boottime": { "secs": 604800, "nanosecs": 0 } } Signed-off-by: Chethan Suresh <[email protected]>
1 parent 369ad5a commit d6c0d52

File tree

9 files changed

+102
-0
lines changed

9 files changed

+102
-0
lines changed

libcontainer/configs/config.go

+3
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ type Config struct {
212212
// RootlessCgroups is set when unlikely to have the full access to cgroups.
213213
// When RootlessCgroups is set, cgroups errors are ignored.
214214
RootlessCgroups bool `json:"rootless_cgroups,omitempty"`
215+
216+
// TimeOffsets specifies the offset for supporting time namespaces.
217+
TimeOffsets map[string]specs.LinuxTimeOffset `json:"timeOffsets,omitempty"`
215218
}
216219

217220
type (

libcontainer/configs/namespaces_linux.go

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const (
1414
NEWIPC NamespaceType = "NEWIPC"
1515
NEWUSER NamespaceType = "NEWUSER"
1616
NEWCGROUP NamespaceType = "NEWCGROUP"
17+
NEWTIME NamespaceType = "NEWTIME"
1718
)
1819

1920
var (
@@ -38,6 +39,8 @@ func NsName(ns NamespaceType) string {
3839
return "uts"
3940
case NEWCGROUP:
4041
return "cgroup"
42+
case NEWTIME:
43+
return "time"
4144
}
4245
return ""
4346
}
@@ -72,6 +75,7 @@ func NamespaceTypes() []NamespaceType {
7275
NEWPID,
7376
NEWNS,
7477
NEWCGROUP,
78+
NEWTIME,
7579
}
7680
}
7781

libcontainer/configs/namespaces_syscall.go

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var namespaceInfo = map[NamespaceType]int{
1717
NEWUTS: unix.CLONE_NEWUTS,
1818
NEWPID: unix.CLONE_NEWPID,
1919
NEWCGROUP: unix.CLONE_NEWCGROUP,
20+
NEWTIME: unix.CLONE_NEWTIME,
2021
}
2122

2223
// CloneFlags parses the container's Namespaces options to set the correct

libcontainer/configs/validate/validator.go

+6
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ func namespaces(config *configs.Config) error {
115115
}
116116
}
117117

118+
if config.Namespaces.Contains(configs.NEWTIME) {
119+
if _, err := os.Stat("/proc/self/timens_offsets"); os.IsNotExist(err) {
120+
return errors.New("time namespaces aren't enabled in the kernel")
121+
}
122+
}
123+
118124
return nil
119125
}
120126

libcontainer/container_linux.go

+38
Original file line numberDiff line numberDiff line change
@@ -2114,6 +2114,16 @@ func encodeIDMapping(idMap []configs.IDMap) ([]byte, error) {
21142114
return data.Bytes(), nil
21152115
}
21162116

2117+
func encodeTimeNs(timeOffsets specs.LinuxTimeOffset) ([]byte, error) {
2118+
data := bytes.NewBuffer(nil)
2119+
line := fmt.Sprintf("%d %d\n", timeOffsets.Secs, timeOffsets.Nanosecs)
2120+
if _, err := data.WriteString(line); err != nil {
2121+
return nil, err
2122+
}
2123+
2124+
return data.Bytes(), nil
2125+
}
2126+
21172127
// netlinkError is an error wrapper type for use by custom netlink message
21182128
// types. Panics with errors are wrapped in netlinkError so that the recover
21192129
// in bootstrapData can distinguish intentional panics.
@@ -2246,6 +2256,34 @@ func (c *Container) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Namespa
22462256
})
22472257
}
22482258

2259+
// write boottime and monotonic offsets
2260+
// 0 is default value in /proc/PID/timens_offsets
2261+
// if 0, do not bootstrap data
2262+
if c.config.TimeOffsets != nil {
2263+
2264+
if c.config.TimeOffsets["boottime"].Nanosecs != 0 || c.config.TimeOffsets["boottime"].Secs != 0 {
2265+
b, err := encodeTimeNs(c.config.TimeOffsets["boottime"])
2266+
if err != nil {
2267+
return nil, err
2268+
}
2269+
r.AddData(&Bytemsg{
2270+
Type: BootTimeNsAttr,
2271+
Value: append([]byte("boottime "), b...),
2272+
})
2273+
}
2274+
2275+
if c.config.TimeOffsets["monotonic"].Nanosecs != 0 || c.config.TimeOffsets["monotonic"].Secs != 0 {
2276+
b, err := encodeTimeNs(c.config.TimeOffsets["monotonic"])
2277+
if err != nil {
2278+
return nil, err
2279+
}
2280+
r.AddData(&Bytemsg{
2281+
Type: MonotonicNsAttr,
2282+
Value: append([]byte("monotonic "), b...),
2283+
})
2284+
}
2285+
}
2286+
22492287
return bytes.NewReader(r.Serialize()), nil
22502288
}
22512289

libcontainer/message_linux.go

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const (
2222
UidmapPathAttr uint16 = 27288
2323
GidmapPathAttr uint16 = 27289
2424
MountSourcesAttr uint16 = 27290
25+
BootTimeNsAttr uint16 = 27291
26+
MonotonicNsAttr uint16 = 27292
2527
)
2628

2729
type Int32msg struct {

libcontainer/nsenter/namespace.h

+3
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,8 @@
2828
#ifndef CLONE_NEWNET
2929
# define CLONE_NEWNET 0x40000000 /* New network namespace */
3030
#endif
31+
#ifndef CLONE_NEWTIME
32+
# define CLONE_NEWTIME 0x00000080 /* New time namespace */
33+
#endif
3134

3235
#endif /* NSENTER_NAMESPACE_H */

libcontainer/nsenter/nsexec.c

+38
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ struct nlconfig_t {
9595
/* Mount sources opened outside the container userns. */
9696
char *mountsources;
9797
size_t mountsources_len;
98+
99+
/* Time NS settings for boottime and monotonic */
100+
char *monotonic;
101+
size_t monotonic_len;
102+
103+
char *boottime;
104+
size_t boottime_len;
98105
};
99106

100107
/*
@@ -112,6 +119,8 @@ struct nlconfig_t {
112119
#define UIDMAPPATH_ATTR 27288
113120
#define GIDMAPPATH_ATTR 27289
114121
#define MOUNT_SOURCES_ATTR 27290
122+
#define BOOTTIME_NS_ATTR 27291
123+
#define MONOTONIC_NS_ATTR 27292
115124

116125
/*
117126
* Use the raw syscall for versions of glibc which don't include a function for
@@ -341,6 +350,8 @@ static int nsflag(char *name)
341350
return CLONE_NEWUSER;
342351
else if (!strcmp(name, "uts"))
343352
return CLONE_NEWUTS;
353+
else if (!strcmp(name, "time"))
354+
return CLONE_NEWTIME;
344355

345356
/* If we don't recognise a name, fallback to 0. */
346357
return 0;
@@ -431,6 +442,14 @@ static void nl_parse(int fd, struct nlconfig_t *config)
431442
config->mountsources = current;
432443
config->mountsources_len = payload_len;
433444
break;
445+
case BOOTTIME_NS_ATTR:
446+
config->boottime = current;
447+
config->boottime_len = payload_len;
448+
break;
449+
case MONOTONIC_NS_ATTR:
450+
config->monotonic = current;
451+
config->monotonic_len = payload_len;
452+
break;
434453
default:
435454
bail("unknown netlink message type %d", nlattr->nla_type);
436455
}
@@ -641,6 +660,17 @@ void try_unshare(int flags, const char *msg)
641660
bail("failed to unshare %s", msg);
642661
}
643662

663+
static void update_timens(char *map, size_t map_len)
664+
{
665+
if (map == NULL || map_len == 0)
666+
return;
667+
write_log(DEBUG, "update /proc/self/timens_offsets to '%s'", map);
668+
if (write_file(map, map_len, "/proc/self/timens_offsets") < 0) {
669+
if (errno != EPERM)
670+
bail("failed to update /proc/self/timens_offsets");
671+
}
672+
}
673+
644674
void nsexec(void)
645675
{
646676
int pipenum;
@@ -1053,6 +1083,14 @@ void nsexec(void)
10531083
bail("failed to sync with parent: SYNC_MOUNTSOURCES_ACK: got %u", s);
10541084
}
10551085

1086+
/*
1087+
* Update timens offsets
1088+
* set boottime and monotonic offsets
1089+
*/
1090+
write_log(DEBUG, "set timens offsets %s", config.boottime);
1091+
update_timens(config.boottime, config.boottime_len);
1092+
update_timens(config.monotonic, config.monotonic_len);
1093+
10561094
/*
10571095
* TODO: What about non-namespace clone flags that we're dropping here?
10581096
*

libcontainer/specconv/spec_linux.go

+7
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func initMaps() {
4949
specs.IPCNamespace: configs.NEWIPC,
5050
specs.UTSNamespace: configs.NEWUTS,
5151
specs.CgroupNamespace: configs.NEWCGROUP,
52+
specs.TimeNamespace: configs.NEWTIME,
5253
}
5354

5455
mountPropagationMapping = map[string]int{
@@ -433,6 +434,11 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
433434
MemBwSchema: spec.Linux.IntelRdt.MemBwSchema,
434435
}
435436
}
437+
438+
// update timens offsets
439+
if spec.Linux.TimeOffsets != nil {
440+
config.TimeOffsets = spec.Linux.TimeOffsets
441+
}
436442
}
437443

438444
// Set the host UID that should own the container's cgroup.
@@ -492,6 +498,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
492498
}
493499
}
494500
}
501+
495502
createHooks(spec, config)
496503
config.Version = specs.Version
497504
return config, nil

0 commit comments

Comments
 (0)