ntp - 创建响应服务器系统时间的简单 NTP 服务器时出现问题
问题描述
我正在尝试构建一个非常简单的 NTP (v3) 服务器,用于接收来自 LAN 上 IP 摄像机的 NTP 请求,以实现时间同步。摄像机与 Internet 断开连接,因此想法是使用本地 PC 服务器作为摄像机的 NTP 服务器。
我尝试了两种不同的方法。
- 编写简单的 UDP 转发到已知的 NTP 服务器(例如 time.windows.com)。这运作良好。
- 编写一个简单的 UDP 服务器,在端口 123 上侦听传入的 NTP 请求,它只返回服务器的系统时间。这对于简单的要求不高的 NTP 客户端(例如物理网络路由器)也很有效,但对于本地 HIKVISION 摄像头来说总是失败。
方法:接收一个48字节的缓冲区。确保偏移量 0 处的字节为 0x1B。将偏移量 0 处的字节转换为 0x1C 并将当前 UTC 时间写入最后 8 个字节作为 NTP 时间戳。这适用于大多数 NTP 客户端,但不适用于海康威视。
相机发送此请求:
1B-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
00-00-00-00-00-00-00-00-61-8C-DE-CA-C3-73-89-DC
最后 8 个字节非零。如果我尝试修改我的 UDP 转发解决方案 [1],以便在转发之前将最后 8 个字节清零,则相机会报告错误。所以事实证明这些位很重要,并且可能具有一些密码意义。
我正在研究 RFC 以试图理解这一点,但我找不到解释。我能找到的任何示例代码都完全忽略了这一点,并走上了简单的路线。
所以问题是......如何解释 NTP 请求的尾随字节以及如何返回正确的 NTP 响应?欢迎提供一些示例代码或资源指针。
解决方案
有关NTP 数据包格式的说明,请参阅从https://www.ietf.org/rfc/rfc1305.pdf第 50 页开始的附录 A。最后 8 个字节应该携带服务器的响应时间戳——这正是您在构建最小响应数据包时将时间戳放入这些字节的原因。
但是,基于此客户端将值放入最后 8 个字节的事实,它看起来想要使用 SNTP(简单 NTP)协议第 5 节中描述的机制,每个https://www.ietf.org/ rfc/rfc2030.txt SNTP 使用与 NTP 相同的数据包格式,但使用字段的方式略有不同。在 SNTP 中,客户端放入字节 40 到 47 的值是客户端当前的时间概念。(在这种情况下,它偏离了一点,AFAICT 的时间戳大约是 1951 年左右。)
如果这是该客户端尝试执行的操作,那么它希望服务器将这 8 个字节复制到字节 24-31(原始时间戳字段),然后将服务器的当前时间写入字节 32-39(接收时间戳)并写入字节 40-47(传输时间戳),并将其作为响应发送。当然,还要继续将响应中的第一个字节改为0x1C,以表明这个数据包来自一个服务器。您还应该将字节 1 中的 Stratum 值设置为合理的非零值,例如 3 或 4。
鉴于客户端的时钟已经关闭了很长时间,它可能需要几轮请求/响应才能使其同步。所以不要指望它的时钟会立即跳到匹配服务器的时钟。(它可能会这样做,但我不会指望它。)
我认为你不需要通过特别对待这个客户来使你的逻辑复杂化。您可能可以将完全相同的逻辑应用于来自客户端的数据包,这些数据包将零放入最后 8 个字节。这只是意味着当您构建对这些客户端的响应时,您将在 Originate Timestamp 字段中复制零。
推荐阅读
- javascript - 在javascript中的入口点元素之后返回多个数字
- sql - 尽管对所有关键变量进行了分组,SQL 还是重复?
- reactjs - 如何将变量传递给组件树中的第一个孩子
- swift - 在我的视图集合中,我添加了一个搜索栏,它过滤了单元格,但是当我单击单元格时,它自己的路径没有改变
- java - 用户验证电子邮件和密码(Java web、servlet)
- google-cloud-platform - 如何使用 Puppet 在 IIS 中创建多个站点?
- javascript - 如何使按钮在动态表中保持相同大小
- c# - 如何使用jquery刷新视图组件而不刷新页面
- android - 如何在 Android 的 billingclient 库中显示用户已购买的产品?
- mongodb - MongoDB - 重命名具有不同结构的不同集合中的键