Skip to content

Commit

Permalink
Support time namespace
Browse files Browse the repository at this point in the history
"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]>
  • Loading branch information
chethanah committed Jul 6, 2023
1 parent 369ad5a commit c6f73ac
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 0 deletions.
3 changes: 3 additions & 0 deletions libcontainer/configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ type Config struct {
// RootlessCgroups is set when unlikely to have the full access to cgroups.
// When RootlessCgroups is set, cgroups errors are ignored.
RootlessCgroups bool `json:"rootless_cgroups,omitempty"`

// TimeOffsets specifies the offset for supporting time namespaces.
TimeOffsets map[string]specs.LinuxTimeOffset `json:"timeOffsets,omitempty"`
}

type (
Expand Down
4 changes: 4 additions & 0 deletions libcontainer/configs/namespaces_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
NEWIPC NamespaceType = "NEWIPC"
NEWUSER NamespaceType = "NEWUSER"
NEWCGROUP NamespaceType = "NEWCGROUP"
NEWTIME NamespaceType = "NEWTIME"
)

var (
Expand All @@ -38,6 +39,8 @@ func NsName(ns NamespaceType) string {
return "uts"
case NEWCGROUP:
return "cgroup"
case NEWTIME:
return "time"
}
return ""
}
Expand Down Expand Up @@ -72,6 +75,7 @@ func NamespaceTypes() []NamespaceType {
NEWPID,
NEWNS,
NEWCGROUP,
NEWTIME,
}
}

Expand Down
1 change: 1 addition & 0 deletions libcontainer/configs/namespaces_syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var namespaceInfo = map[NamespaceType]int{
NEWUTS: unix.CLONE_NEWUTS,
NEWPID: unix.CLONE_NEWPID,
NEWCGROUP: unix.CLONE_NEWCGROUP,
NEWTIME: unix.CLONE_NEWTIME,
}

// CloneFlags parses the container's Namespaces options to set the correct
Expand Down
6 changes: 6 additions & 0 deletions libcontainer/configs/validate/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ func namespaces(config *configs.Config) error {
}
}

if config.Namespaces.Contains(configs.NEWTIME) {
if _, err := os.Stat("/proc/self/timens_offsets"); os.IsNotExist(err) {
return errors.New("time namespaces aren't enabled in the kernel")
}
}

return nil
}

Expand Down
39 changes: 39 additions & 0 deletions libcontainer/container_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2114,6 +2114,16 @@ func encodeIDMapping(idMap []configs.IDMap) ([]byte, error) {
return data.Bytes(), nil
}

func encodeTimeNs(timeOffsets specs.LinuxTimeOffset) ([]byte, error) {
data := bytes.NewBuffer(nil)
line := fmt.Sprintf("%d %d\n", timeOffsets.Secs, timeOffsets.Nanosecs)
if _, err := data.WriteString(line); err != nil {
return nil, err
}

return data.Bytes(), nil
}

// netlinkError is an error wrapper type for use by custom netlink message
// types. Panics with errors are wrapped in netlinkError so that the recover
// in bootstrapData can distinguish intentional panics.
Expand Down Expand Up @@ -2192,6 +2202,7 @@ func (c *Container) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Namespa
if err != nil {
return nil, err
}
logrus.Printf("GidmapAttr: %+v", string(b))
r.AddData(&Bytemsg{
Type: GidmapAttr,
Value: b,
Expand Down Expand Up @@ -2246,6 +2257,34 @@ func (c *Container) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Namespa
})
}

// write boottime and monotonic offsets
// 0 is default value in /proc/PID/timens_offsets
// if 0, do not bootstrap data
if c.config.TimeOffsets != nil {

if c.config.TimeOffsets["boottime"].Nanosecs != 0 || c.config.TimeOffsets["boottime"].Secs != 0 {
b, err := encodeTimeNs(c.config.TimeOffsets["boottime"])
if err != nil {
return nil, err
}
r.AddData(&Bytemsg{
Type: BootTimeNsAttr,
Value: append([]byte("boottime "), b...),
})
}

if c.config.TimeOffsets["monotonic"].Nanosecs != 0 || c.config.TimeOffsets["monotonic"].Secs != 0 {
b, err := encodeTimeNs(c.config.TimeOffsets["monotonic"])
if err != nil {
return nil, err
}
r.AddData(&Bytemsg{
Type: MonotonicNsAttr,
Value: append([]byte("monotonic "), b...),
})
}
}

return bytes.NewReader(r.Serialize()), nil
}

Expand Down
2 changes: 2 additions & 0 deletions libcontainer/message_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const (
UidmapPathAttr uint16 = 27288
GidmapPathAttr uint16 = 27289
MountSourcesAttr uint16 = 27290
BootTimeNsAttr uint16 = 27291
MonotonicNsAttr uint16 = 27292
)

type Int32msg struct {
Expand Down
3 changes: 3 additions & 0 deletions libcontainer/nsenter/namespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@
#ifndef CLONE_NEWNET
# define CLONE_NEWNET 0x40000000 /* New network namespace */
#endif
#ifndef CLONE_NEWTIME
# define CLONE_NEWTIME 0x00000080 /* New time namespace */
#endif

#endif /* NSENTER_NAMESPACE_H */
38 changes: 38 additions & 0 deletions libcontainer/nsenter/nsexec.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ struct nlconfig_t {
/* Mount sources opened outside the container userns. */
char *mountsources;
size_t mountsources_len;

/* Time NS settings for boottime and monotonic */
char *monotonic;
size_t monotonic_len;

char *boottime;
size_t boottime_len;
};

/*
Expand All @@ -112,6 +119,8 @@ struct nlconfig_t {
#define UIDMAPPATH_ATTR 27288
#define GIDMAPPATH_ATTR 27289
#define MOUNT_SOURCES_ATTR 27290
#define BOOTTIME_NS_ATTR 27291
#define MONOTONIC_NS_ATTR 27292

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

/* If we don't recognise a name, fallback to 0. */
return 0;
Expand Down Expand Up @@ -431,6 +442,14 @@ static void nl_parse(int fd, struct nlconfig_t *config)
config->mountsources = current;
config->mountsources_len = payload_len;
break;
case BOOTTIME_NS_ATTR:
config->boottime = current;
config->boottime_len = payload_len;
break;
case MONOTONIC_NS_ATTR:
config->monotonic = current;
config->monotonic_len = payload_len;
break;
default:
bail("unknown netlink message type %d", nlattr->nla_type);
}
Expand Down Expand Up @@ -641,6 +660,17 @@ void try_unshare(int flags, const char *msg)
bail("failed to unshare %s", msg);
}

static void update_timens(char *map, size_t map_len)
{
if (map == NULL || map_len == 0)
return;
write_log(DEBUG, "update /proc/self/timens_offsets to '%s'", map);
if (write_file(map, map_len , "/proc/self/timens_offsets") < 0) {
if (errno != EPERM)
bail("failed to update /proc/self/timens_offsets");
}
}

void nsexec(void)
{
int pipenum;
Expand Down Expand Up @@ -1053,6 +1083,14 @@ void nsexec(void)
bail("failed to sync with parent: SYNC_MOUNTSOURCES_ACK: got %u", s);
}

/*
* Update timens offsets
* set boottime and monotonic offsets
*/
write_log(DEBUG, "set timens offsets %s", config.boottime);
update_timens(config.boottime, config.boottime_len);
update_timens(config.monotonic, config.monotonic_len);

/*
* TODO: What about non-namespace clone flags that we're dropping here?
*
Expand Down
7 changes: 7 additions & 0 deletions libcontainer/specconv/spec_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func initMaps() {
specs.IPCNamespace: configs.NEWIPC,
specs.UTSNamespace: configs.NEWUTS,
specs.CgroupNamespace: configs.NEWCGROUP,
specs.TimeNamespace: configs.NEWTIME,
}

mountPropagationMapping = map[string]int{
Expand Down Expand Up @@ -492,6 +493,12 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
}
}
}

// update timens offsets
if spec.Linux.TimeOffsets != nil {
config.TimeOffsets = spec.Linux.TimeOffsets
}

createHooks(spec, config)
config.Version = specs.Version
return config, nil
Expand Down

0 comments on commit c6f73ac

Please sign in to comment.