/
config.go
184 lines (154 loc) · 4.4 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
Copyright (c) Facebook, Inc. and its affiliates.
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.
*/
/*
Package server implements simple Unicast PTP UDP server.
*/
package server
import (
"errors"
"fmt"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
ptp "github.com/facebook/time/ptp/protocol"
"github.com/facebook/time/timestamp"
"golang.org/x/sys/unix"
yaml "gopkg.in/yaml.v2"
)
var errInsaneUTCoffset = errors.New("UTC offset is outside of sane range")
// dcMux is a dynamic config mutex
var dcMux = sync.Mutex{}
// StaticConfig is a set of static options which require a server restart
type StaticConfig struct {
ConfigFile string
DebugAddr string
DomainNumber uint
DrainFileName string
DSCP int
Interface string
IP net.IP
LogLevel string
MonitoringPort int
PidFile string
QueueSize int
RecvWorkers int
SendWorkers int
TimestampType timestamp.Timestamp
UndrainFileName string
}
// DynamicConfig is a set of dynamic options which don't need a server restart
type DynamicConfig struct {
// ClockAccuracy to report via announce messages. Time Accurate within 100ns
ClockAccuracy ptp.ClockAccuracy
// ClockClass to report via announce messages. 6 - Locked with Primary Reference Clock
ClockClass ptp.ClockClass
// DrainInterval is an interval for drain checks
DrainInterval time.Duration
// MaxSubDuration is a maximum sync/announce/delay_resp subscription duration
MaxSubDuration time.Duration
// MetricInterval is an interval of resetting metrics
MetricInterval time.Duration
// MinSubInterval is a minimum interval of the sync/announce subscription messages
MinSubInterval time.Duration
// UTCOffset is a current UTC offset.
UTCOffset time.Duration
}
// Config is a server config structure
type Config struct {
StaticConfig
DynamicConfig
clockIdentity ptp.ClockIdentity
}
// UTCOffsetSanity checks if UTC offset value has an adequate value
// As of Apr 2022 TAI UTC offset is 37 seconds
func (dc *DynamicConfig) UTCOffsetSanity() error {
if dc.UTCOffset < 30*time.Second || dc.UTCOffset > 50*time.Second {
return errInsaneUTCoffset
}
return nil
}
// ReadDynamicConfig reads dynamic config from the file
func ReadDynamicConfig(path string) (*DynamicConfig, error) {
dc := &DynamicConfig{}
cData, err := os.ReadFile(path)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(cData, &dc)
if err != nil {
return nil, err
}
if err := dc.UTCOffsetSanity(); err != nil {
return nil, err
}
return dc, nil
}
// Write dynamic config to a file
func (dc *DynamicConfig) Write(path string) error {
d, err := yaml.Marshal(&dc)
if err != nil {
return err
}
return os.WriteFile(path, d, 0644)
}
// IfaceHasIP checks if selected IP is on interface
func (c *Config) IfaceHasIP() (bool, error) {
ips, err := ifaceIPs(c.Interface)
if err != nil {
return false, err
}
for _, ip := range ips {
if c.IP.Equal(ip) {
return true, nil
}
}
return false, nil
}
// CreatePidFile creates a pid file in a defined location
func (c *Config) CreatePidFile() error {
return os.WriteFile(c.PidFile, []byte(fmt.Sprintf("%d\n", unix.Getpid())), 0644)
}
// DeletePidFile deletes a pid file from a defined location
func (c *Config) DeletePidFile() error {
return os.Remove(c.PidFile)
}
// ReadPidFile read a pid file from a path location and returns a pid
func ReadPidFile(path string) (int, error) {
content, err := os.ReadFile(path)
if err != nil {
return 0, err
}
return strconv.Atoi(strings.Replace(string(content), "\n", "", -1))
}
// ifaceIPs gets all IPs on the specified interface
func ifaceIPs(iface string) ([]net.IP, error) {
i, err := net.InterfaceByName(iface)
if err != nil {
return nil, err
}
addrs, err := i.Addrs()
if err != nil {
return nil, err
}
res := []net.IP{}
for _, addr := range addrs {
ip := addr.(*net.IPNet).IP
res = append(res, ip)
}
res = append(res, net.IPv6zero)
res = append(res, net.IPv4zero)
return res, nil
}