Commit d413fcb4 authored by Arnd Bergmann's avatar Arnd Bergmann

packet: clarify timestamp overflow

The memory mapped packet socket data structure in version 1 through 3
all contain 32-bit second values for the packet time stamps, which makes
them suffer from the overflow of time_t in y2038 or y2106 (depending
on whether user space interprets the value as signed or unsigned).

The implementation uses the deprecated getnstimeofday() function.

In order to get rid of that, this changes the code to use
ktime_get_real_ts64() as a replacement, documenting the nature of the
overflow. As long as the user applications treat the timestamps as
unsigned, or only use the difference between timestamps, they are
fine, and changing the timestamps to 64-bit wouldn't require a more
invasive user space API change.

Note: a lot of other APIs suffer from incompatible structures when
time_t gets redefined to 64-bit in 32-bit user space, but this one
does not.
Acked-by: default avatarWillem de Bruijn <willemb@google.com>
Link: https://lore.kernel.org/lkml/CAF=yD-Jomr-gWSR-EBNKnSpFL46UeG564FLfqTCMNEm-prEaXA@mail.gmail.com/T/#uSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parent 352c912b
...@@ -408,17 +408,17 @@ static int __packet_get_status(const struct packet_sock *po, void *frame) ...@@ -408,17 +408,17 @@ static int __packet_get_status(const struct packet_sock *po, void *frame)
} }
} }
static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec *ts, static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts,
unsigned int flags) unsigned int flags)
{ {
struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
if (shhwtstamps && if (shhwtstamps &&
(flags & SOF_TIMESTAMPING_RAW_HARDWARE) && (flags & SOF_TIMESTAMPING_RAW_HARDWARE) &&
ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts)) ktime_to_timespec64_cond(shhwtstamps->hwtstamp, ts))
return TP_STATUS_TS_RAW_HARDWARE; return TP_STATUS_TS_RAW_HARDWARE;
if (ktime_to_timespec_cond(skb->tstamp, ts)) if (ktime_to_timespec64_cond(skb->tstamp, ts))
return TP_STATUS_TS_SOFTWARE; return TP_STATUS_TS_SOFTWARE;
return 0; return 0;
...@@ -428,13 +428,20 @@ static __u32 __packet_set_timestamp(struct packet_sock *po, void *frame, ...@@ -428,13 +428,20 @@ static __u32 __packet_set_timestamp(struct packet_sock *po, void *frame,
struct sk_buff *skb) struct sk_buff *skb)
{ {
union tpacket_uhdr h; union tpacket_uhdr h;
struct timespec ts; struct timespec64 ts;
__u32 ts_status; __u32 ts_status;
if (!(ts_status = tpacket_get_timestamp(skb, &ts, po->tp_tstamp))) if (!(ts_status = tpacket_get_timestamp(skb, &ts, po->tp_tstamp)))
return 0; return 0;
h.raw = frame; h.raw = frame;
/*
* versions 1 through 3 overflow the timestamps in y2106, since they
* all store the seconds in a 32-bit unsigned integer.
* If we create a version 4, that should have a 64-bit timestamp,
* either 64-bit seconds + 32-bit nanoseconds, or just 64-bit
* nanoseconds.
*/
switch (po->tp_version) { switch (po->tp_version) {
case TPACKET_V1: case TPACKET_V1:
h.h1->tp_sec = ts.tv_sec; h.h1->tp_sec = ts.tv_sec;
...@@ -774,8 +781,8 @@ static void prb_close_block(struct tpacket_kbdq_core *pkc1, ...@@ -774,8 +781,8 @@ static void prb_close_block(struct tpacket_kbdq_core *pkc1,
* It shouldn't really happen as we don't close empty * It shouldn't really happen as we don't close empty
* blocks. See prb_retire_rx_blk_timer_expired(). * blocks. See prb_retire_rx_blk_timer_expired().
*/ */
struct timespec ts; struct timespec64 ts;
getnstimeofday(&ts); ktime_get_real_ts64(&ts);
h1->ts_last_pkt.ts_sec = ts.tv_sec; h1->ts_last_pkt.ts_sec = ts.tv_sec;
h1->ts_last_pkt.ts_nsec = ts.tv_nsec; h1->ts_last_pkt.ts_nsec = ts.tv_nsec;
} }
...@@ -805,7 +812,7 @@ static void prb_thaw_queue(struct tpacket_kbdq_core *pkc) ...@@ -805,7 +812,7 @@ static void prb_thaw_queue(struct tpacket_kbdq_core *pkc)
static void prb_open_block(struct tpacket_kbdq_core *pkc1, static void prb_open_block(struct tpacket_kbdq_core *pkc1,
struct tpacket_block_desc *pbd1) struct tpacket_block_desc *pbd1)
{ {
struct timespec ts; struct timespec64 ts;
struct tpacket_hdr_v1 *h1 = &pbd1->hdr.bh1; struct tpacket_hdr_v1 *h1 = &pbd1->hdr.bh1;
smp_rmb(); smp_rmb();
...@@ -818,7 +825,7 @@ static void prb_open_block(struct tpacket_kbdq_core *pkc1, ...@@ -818,7 +825,7 @@ static void prb_open_block(struct tpacket_kbdq_core *pkc1,
BLOCK_NUM_PKTS(pbd1) = 0; BLOCK_NUM_PKTS(pbd1) = 0;
BLOCK_LEN(pbd1) = BLK_PLUS_PRIV(pkc1->blk_sizeof_priv); BLOCK_LEN(pbd1) = BLK_PLUS_PRIV(pkc1->blk_sizeof_priv);
getnstimeofday(&ts); ktime_get_real_ts64(&ts);
h1->ts_first_pkt.ts_sec = ts.tv_sec; h1->ts_first_pkt.ts_sec = ts.tv_sec;
h1->ts_first_pkt.ts_nsec = ts.tv_nsec; h1->ts_first_pkt.ts_nsec = ts.tv_nsec;
...@@ -2168,7 +2175,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -2168,7 +2175,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
unsigned long status = TP_STATUS_USER; unsigned long status = TP_STATUS_USER;
unsigned short macoff, netoff, hdrlen; unsigned short macoff, netoff, hdrlen;
struct sk_buff *copy_skb = NULL; struct sk_buff *copy_skb = NULL;
struct timespec ts; struct timespec64 ts;
__u32 ts_status; __u32 ts_status;
bool is_drop_n_account = false; bool is_drop_n_account = false;
bool do_vnet = false; bool do_vnet = false;
...@@ -2300,7 +2307,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -2300,7 +2307,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
skb_copy_bits(skb, 0, h.raw + macoff, snaplen); skb_copy_bits(skb, 0, h.raw + macoff, snaplen);
if (!(ts_status = tpacket_get_timestamp(skb, &ts, po->tp_tstamp))) if (!(ts_status = tpacket_get_timestamp(skb, &ts, po->tp_tstamp)))
getnstimeofday(&ts); ktime_get_real_ts64(&ts);
status |= ts_status; status |= ts_status;
......
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