Files
open5gs/lib/core/ogs-time.c
Pau Espin Pedrol f060da45d5 [CORE] time: Add APIs to manage NTP 32-bit timestamps (#1439)
These will be further needed in PFCP in the future, as well as in other
Diameter based interfaces (such as Gy).
Let's put all implementation details in APIs so that devs don't need to
care about those details every time.
2022-03-16 11:25:30 +09:00

326 lines
8.5 KiB
C

/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright 2013 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright Beman Dawes 2008
* Copyright 2009-2010 Vicente J. Botet Escriba
*
* Distributed under the Boost Software License, Version 1.0.
* See http://www.boost.org/LICENSE_1_0.txt
*/
/*
* Copyright (C) 2019-2020 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef __APPLE__
#include <mach/clock.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#include "core-config-private.h"
#include "ogs-core.h"
/*
* The following code is stolen from mongodb-c-driver
* https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-clock.c
*/
int ogs_gettimeofday(struct timeval *tv)
{
#if defined(_WIN32)
#if defined(_MSC_VER)
#define DELTA_EPOCH_IN_MICROSEC 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSEC 11644473600000000ULL
#endif
FILETIME ft;
uint64_t tmp = 0;
/*
* The const value is shamelessy stolen from
* http://www.boost.org/doc/libs/1_55_0/boost/chrono/detail/inlined/win/chrono.hpp
*
* File times are the number of 100 nanosecond intervals elapsed since
* 12:00 am Jan 1, 1601 UTC. I haven't check the math particularly hard
*
* ... good luck
*/
if (tv) {
GetSystemTimeAsFileTime (&ft);
/* pull out of the filetime into a 64 bit uint */
tmp |= ft.dwHighDateTime;
tmp <<= 32;
tmp |= ft.dwLowDateTime;
/* convert from 100's of nanosecs to microsecs */
tmp /= 10;
/* adjust to unix epoch */
tmp -= DELTA_EPOCH_IN_MICROSEC;
tv->tv_sec = (long) (tmp / 1000000UL);
tv->tv_usec = (long) (tmp % 1000000UL);
}
return 0;
#else
int rc = gettimeofday(tv, NULL);
ogs_assert(rc == 0);
return 0;
#endif
}
ogs_time_t ogs_time_now(void)
{
int rc;
struct timeval tv;
rc = ogs_gettimeofday(&tv);
ogs_assert(rc == 0);
return tv.tv_sec * OGS_USEC_PER_SEC + tv.tv_usec;
}
/* The following code is stolen from APR library */
int ogs_time_from_lt(ogs_time_t *t, struct tm *tm, int tm_usec)
{
ogs_time_t year = tm->tm_year;
ogs_time_t days;
static const int dayoffset[12] =
{306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
if (tm->tm_mon < 0 || tm->tm_mon >= 12)
return OGS_ERROR;
/* shift new year to 1st March in order to make leap year calc easy */
if (tm->tm_mon < 2)
year--;
/* Find number of days since 1st March 1900 (in the Gregorian calendar). */
days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
days += dayoffset[tm->tm_mon] + tm->tm_mday - 1;
days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
days = ((days * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
if (days < 0) {
return OGS_ERROR;
}
*t = days * OGS_USEC_PER_SEC + tm_usec;
return OGS_OK;
}
int ogs_time_from_gmt(ogs_time_t *t, struct tm *tm, int tm_usec)
{
int status = ogs_time_from_lt(t, tm, tm_usec);
if (status == OGS_OK)
*t -= (ogs_time_t) tm->tm_gmtoff * OGS_USEC_PER_SEC;
return status;
}
/* RFC 5905 A.1.1, A.4
* PFCP entity uses NTP timestamp(1900), but Open5GS uses UNIX(1970).
*
* One is the offset between the two epochs.
* Unix uses an epoch located at 1/1/1970-00:00h (UTC) and
* NTP uses 1/1/1900-00:00h. This leads to an offset equivalent
* to 70 years in seconds (there are 17 leap years
* between the two dates so the offset is
*
* (70*365 + 17)*86400 = 2208988800
*
* to be substracted from NTP time to get Unix struct timeval.
*/
uint32_t ogs_time_ntp32_now(void)
{
int rc;
struct timeval tv;
rc = ogs_gettimeofday(&tv);
ogs_assert(rc == 0);
return ogs_time_to_ntp32(tv.tv_sec * OGS_USEC_PER_SEC + tv.tv_usec);
}
ogs_time_t ogs_time_from_ntp32(uint32_t ntp_timestamp)
{
if (ntp_timestamp < OGS_1970_1900_SEC_DIFF)
return 0;
return (ntp_timestamp - OGS_1970_1900_SEC_DIFF) * OGS_USEC_PER_SEC;
}
uint32_t ogs_time_to_ntp32(ogs_time_t time)
{
return (time / OGS_USEC_PER_SEC) + OGS_1970_1900_SEC_DIFF;
}
int ogs_timezone(void)
{
#if defined(_WIN32)
u_long n;
TIME_ZONE_INFORMATION tz;
n = GetTimeZoneInformation(&tz);
switch (n) {
case TIME_ZONE_ID_UNKNOWN:
/* Bias = UTC - local time in minutes
* tm_gmtoff is seconds east of UTC
*/
return tz.Bias * -60;
case TIME_ZONE_ID_STANDARD:
return (tz.Bias + tz.StandardBias) * -60;
case TIME_ZONE_ID_DAYLIGHT:
return (tz.Bias + tz.DaylightBias) * -60;
default:
ogs_assert_if_reached();
return 0;
}
#else
struct timeval tv;
struct tm tm;
int ret;
ret = ogs_gettimeofday(&tv);
ogs_assert(ret == 0);
ogs_localtime(tv.tv_sec, &tm);
return tm.tm_gmtoff;
#endif
}
ogs_time_t ogs_get_monotonic_time(void)
{
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ((ts.tv_sec * 1000000UL) + (ts.tv_nsec / 1000UL));
#elif defined(__APPLE__)
static mach_timebase_info_data_t info = {0};
static double ratio = 0.0;
if (!info.denom) {
/* the value from mach_absolute_time () * info.numer / info.denom
* is in nano seconds. So we have to divid by 1000.0 to get micro
* seconds*/
mach_timebase_info(&info);
ratio = (double) info.numer / (double) info.denom / 1000.0;
}
return mach_absolute_time() * ratio;
#elif defined(_WIN32)
/* Despite it's name, this is in milliseconds! */
ogs_time_t ticks = GetTickCount64();
return (ticks * 1000L);
#elif defined(__hpux__)
ogs_time_t nanosec = gethrtime();
return (nanosec / 1000UL);
#else
#warning "Monotonic clock is not yet supported on your platform."
struct timeval tv;
ogs_gettimeofday(&tv);
return (tv.tv_sec * 1000000UL) + tv.tv_usec;
#endif
}
void ogs_localtime(time_t s, struct tm *tm)
{
ogs_assert(tm);
memset(tm, 0, sizeof(*tm));
#if (HAVE_LOCALTIME_R)
(void)localtime_r(&s, tm);
#else
struct tm *t;
t = localtime(&s);
*tm = *t;
#endif
}
void ogs_gmtime(time_t s, struct tm *tm)
{
ogs_assert(tm);
memset(tm, 0, sizeof(*tm));
#if (HAVE_LOCALTIME_R)
(void)gmtime_r(&s, tm);
#else
struct tm *t;
t = gmtime(&s);
*tm = *t;
#endif
}
void ogs_msleep(time_t msec)
{
#if defined(_WIN32)
Sleep(msec);
#else
ogs_usleep(msec * 1000);
#endif
}
void ogs_usleep(time_t usec)
{
#if defined(_WIN32)
Sleep(usec ? (1 + (usec - 1) / 1000) : 0);
#else
struct timespec req, rem;
req.tv_sec = usec / OGS_USEC_PER_SEC;
req.tv_nsec = (usec % OGS_USEC_PER_SEC) * 1000;
while (nanosleep(&req, &rem) == -1 && errno == EINTR)
req = rem;
#endif
}