Jumbo Frame(巨帧)
IEEE 802.3 以太网标准仅规定支持 1500Byte 的帧 MTU, 总计 1518Byte 的帧大小.(使用 IEEE 802.1Q VLAN/QoS 标签时, 增加至 1522Byte)而巨型帧往往采用 9000Byte 的帧 MTU, 合计 9018/9022Byte 的帧大小.
目前巨型帧尚未成为官方的 IEEE 802.3 以太网标准的一部分. 所以不同硬件厂商的设备支持程度可能不尽相同.
使用巨型帧, 增大的有效报文长度提升了带宽使用效率的提升(如下图). 与此同时, 增长的报文也带来传输时延的增加, 时延敏感型数据并不适合使用巨型帧传输.
neutron 中的 MTU 配置项
从配置项的描述总结而言, global_physnet_mtu 与 physical_network_mtus 共同定义了 underlay physical network 的 MTU,path_mtu 定义了 overlay network 的 MTU.
** 调整 MTU 的 3 个用例
单 MTU 值物理网络体系 **
在 neutron.conf 中
- [DEFAULT]
- global_physnet_mtu?=?900
在 ml2.INI 中
- [ml2]??
- path_mtu?=?9000
该配置定义了所有 underlay 网络 (flat,vlan) 与 overlay 网络 (vxlan,gre) 的 MTU 值均为 9000.
多 MTU 值物理网络体系
在 neutron.conf 中
- [DEFAULT]??
- global_physnet_mtu?=?9000?
在 ml2.INI 中
- ? [ovs]??
- ??bridge_mappings?=?provider1:eth1,provider2:eth2,provider3:eth3??
- ? [ml2]??
- ??physical_network_mtus?=?provider2:4000,provider3:1500??
- ??path_mtu?=?9000??
该配置定义了 underlay 网络 provider2 的 MTU 值为 4000,provider3 的 MTU 值为 1500, 其他如 provider1 的 MTU 值为 9000. 而 overlay 网络的 MTU 值为 9000.
Overlay 网络 MTU
在 neutron.conf 中
- [DEFAULT]??
- 2.?global_physnet_mtu?=?9000?
在 ml2.INI 中
- [ml2]??
- 2.?path_mtu?=?4000?
该配置定义了所有 underlay 网络 MTU 值为 9000,overlay 网络的 MTU 值均为 4000.
代码浅析
创建 network resource 时的 MTU 处理
flat 和 vlan 网络, 根据实际的物理网络映射与 physical_network_mtus,global_physnet_mtu 信息, 获取最小可用 MTU 值.
- ??def?get_deployment_physnet_mtu():??
- ??return?cfg.CONF.global_physnet_mtu??
- ? ??
- ??class?BaseTypeDriver(API.ML2TypeDriver):??
- ??def?init(self):??
- ??try:??
- ? self.physnet_mtus = helpers.parse_mappings(??
- ? cfg.CONF.ml2.physical_network_mtus, unique_values=False??
- ? ? )??
- ??except?Exception as e:??
- ? LOG.error("Failed to parse physical_network_mtus: %s", e)??
- ? self.physnet_mtus = []??
- ? ?
- ??def?get_mtu(self, physical_network=None):??
- ???return?p_utils.get_deployment_physnet_mtu()??
- ? ?
- ?class?FlatTypeDriver(helpers.BaseTypeDriver):??
- ? ? ?...??
- ??def?get_mtu(self, physical_network):??
- ? seg_mtu = super(FlatTypeDriver, self).get_mtu()??
- ??mtu?= []??
- ? if seg_mtu> 0:??
- ? ?mtu.append(seg_mtu)??
- ???if?physical_network in self.physnet_mtus:??
- ? mtu.append(int(self.physnet_mtus[physical_network]))??
- ??return?min(mtu) if mtu else 0??
- ? ?
- ?class?VlanTypeDriver(helpers.SegmentTypeDriver):??
- ? ? ?...??
- ??def?get_mtu(self, physical_network):??
- ? seg_mtu = super(VlanTypeDriver, self).get_mtu()??
- ? mtu = []??
- ??if?seg_mtu> 0:??
- ? mtu.append(seg_mtu)??
- ??if?physical_network in self.physnet_mtus:??
- ? mtu.append(int(self.physnet_mtus[physical_network]))??
- ??return?min(mtu) if mtu else 0??
Geneve,Gre,Vxlan 类型网络, 则根据 global_physnet_mtu 与 path_mtu 中选取最小的可用 MTU 值, 减去各类型报文头部开销, 获取实际可用 MTU 值.
- 1.? class _TunnelTypeDriverBase(helpers.SegmentTypeDriver):??
- 2.? ...??
- 3.? def get_mtu(self, physical_network=None):??
- 4.? seg_mtu = super(_TunnelTypeDriverBase, self).get_mtu()??
- 5.? mtu = []??
- 6.? if seg_mtu> 0:??
- 7.? mtu.append(seg_mtu)??
- 8.? if cfg.CONF.ml2.path_mtu> 0:??
- 9.? mtu.append(cfg.CONF.ml2.path_mtu)??
- 10.? version = cfg.CONF.ml2.overlay_ip_version??
- 11.? ip_header_length = p_const.IP_HEADER_LENGTH[version]??
- return min(mtu) - ip_header_length if mtu else 0??
- 13.? ?
- class GeneveTypeDriver(type_tunnel.EndpointTunnelTypeDriver):??
- 15.? ...??
- def get_mtu(self, physical_network=None):??
- mtu = super(GeneveTypeDriver, self).get_mtu()??
- return mtu - self.max_encap_size if mtu else 0??
- 19.? ?
- class GreTypeDriver(type_tunnel.EndpointTunnelTypeDriver):??
- 21.? ? ?...??
- def get_mtu(self, physical_network=None):??
- 23.? mtu = super(GreTypeDriver, self).get_mtu(physical_network)??
- 24.? return mtu - p_const.GRE_ENCAP_OVERHEAD if mtu else 0??
- 25.? ? ? ? ? ?
- class VxlanTypeDriver(type_tunnel.EndpointTunnelTypeDriver):??
- 27.? ...??
- def get_mtu(self, physical_network=None):??
- mtu = super(VxlanTypeDriver, self).get_mtu()??
- return mtu - p_const.VXLAN_ENCAP_OVERHEAD if mtu else 0??
在用户实际创建 network 资源时, 若未显式指定网络 MTU 值, 则使用该网络类型下系统定义的最大可用 MTU. 若显式指定 MTU,neutron 会检查用户定义 MTU 是否小于等于该网络类型下系统定义的最大可用 MTU.
- 1.? def _get_network_mtu(self, network_db, validate=True):??
- 2.? mtus = []??
- 3.? ...??
- 4.? for s in segments:??
- 5.? segment_type = s.get('network_type')??
- 6.? if segment_type is None:??
- 7.? ?...??
- 8.? else:??
- 9.? mtu = type_driver.get_mtu(s['physical_network'])??
- Some drivers, like 'local', may return None; the assumption??
- 11.? # then is that for the segment type, MTU has no meaning or??
- unlimited, and so we should then ignore those values.??
- 13.? if mtu:??
- 14.? mtus.append(mtu)??
- 15.? ?
- max_mtu = min(mtus) if mtus else p_utils.get_deployment_physnet_mtu()??
- net_mtu = network_db.get('mtu')??
- 18.? ?
- 19.? if validate:??
- 20.? # validate that requested mtu conforms to allocated segments??
- 21.? if net_mtu and max_mtu and max_mtu <net_mtu:??
- msg = _("Requested MTU is too big, maximum is %d") % max_mtu??
- raise exc.InvalidInput(error_message=msg)??
- 24.? ?
- 25.? # if mtu is not set in database, use the maximum possible??
- return net_mtu or max_mtu??
虚拟机 tap 设置 MTU
在使用 Linux Bridge 实现的 Neutron 网络中, Linux Bridge Agent 在侦测到新的 device 后, 会通过 ip link set 操作, 根据 network 中的 MTU 值, 设置虚拟机绑定至 Linux Bridge 的 tap 设备的 MTU 值. 反观 Openvswitch 实现的网络中却没有相关的设置. 实际在使用过程中需要通过 ovs-vsctl set Interface <tap name> mtu_request=<MTU Value > 命令人工去设置 tap 设备的 MTU 值.
- ? class LinuxBridgeManager(amb.CommonAgentManagerBase):??
- ? def plug_interface(self, network_id, network_segment, tap_name,??
- ? device_owner):??
- ? return self.add_tap_interface(network_id, network_segment.network_type,??
- ? network_segment.physical_network,??
- ? network_segment.segmentation_id,??
- ? tap_name, device_owner,??
- ? network_segment.mtu)??
- ? ??
- ? def _set_tap_mtu(self, tap_device_name, mtu):??
- ip_lib.IPDevice(tap_device_name).link.set_mtu(mtu)??
网络设备 tap 设置 MTU
dhcp 和 router 相关的 tap 设备在 plug 时, neutron 会根据网络的 MTU, 在各 tap 设备所在的 namespace 内运行 "ip link set <tap name> mtu <MTU value>" 设置 tap 设备的 MTU 值.
- 1.? class OVSInterfaceDriver(LinuxInterfaceDriver):??
- def plug_new(self, network_id, port_id, device_name, mac_address,??
- 3.? bridge=None, namespace=None, prefix=None, mtu=None):??
- 4.? ...??
- 5.? # NOTE(ihrachys): the order here is significant: we must set MTU after??
- 6.? # the device is moved into a namespace, otherwise OVS bridge does not??
- 7.? # allow to set MTU that is higher than the least of all device MTUs on??
- 8.? # the bridge??
- 9.? if mtu:??
- 10.? self.set_mtu(device_name, mtu, namespace=namespace, prefix=prefix)??
- 11.? else:??
- 12.? LOG.warning("No MTU configured for port %s", port_id)??
- 13.? ...??
- 14.? ?
- def set_mtu(self, device_name, mtu, namespace=None, prefix=None):??
- 16.? if self.conf.ovs_use_veth:??
- tap_name = self._get_tap_name(device_name, prefix)??
- root_dev, ns_dev = _get_veth(??
- 19.? tap_name, device_name, namespace2=namespace)??
- 20.? root_dev.link.set_mtu(mtu)??
- else:??
- ns_dev = ip_lib.IPWrapper(namespace=namespace).device(device_name)??
- 23.? ns_dev.link.set_mtu(mtu)??
- 24.? ?
- class IpLinkCommand(IpDeviceCommandBase):??
- COMMAND = 'link'??
- 27.? ? ?...??
- 28.? def set_mtu(self, mtu_size):??
- 29.? self._as_root([], ('set', self.name, 'mtu', mtu_size))?
bridge 间 veth 设置 MTU
Openstack 从 J 版以后, neutron 使用 ovs patch port 代替了 Linux veth 实现 OVS 网桥之间的连接(出于性能提升的目的). 但依旧保留了 veth 连接的方式. 在 openvswitch_agent.INI 中可以通过配置 use_veth_interconnection=true 启用 veth 连接网桥的功能. 如果开启这项配置, 默认的 veth_mtu 值为 9000. 当配置链路 MTU 大于 9000 时, 需要修改 openvswitch_agent.INI 配置文件中 veth_mtu 的值, 以免发生瓶颈效应.
- ? class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,??
- ? dvr_rpc.DVRAgentRpcCallbackMixin):??
- ? def init(self, bridge_classes, ext_manager, conf=None):??
- ? ...??
- ? self.use_veth_interconnection = ovs_conf.use_veth_interconnection??
- ? self.veth_mtu = agent_conf.veth_mtu??
- ? ?...??
- ? def setup_physical_bridges(self, bridge_mappings):??
- ? ?'''''Setup the physical network bridges.?
- ??
- ? Creates physical network bridges and links them to the?
- ? integration bridge using veths or patch ports.?
- ??
- ? ?:param bridge_mappings: map physical network names to bridge names.?
- ? ?'''??
- ? self.phys_brs = {}??
- ? self.int_ofports = {}??
- ? self.phys_ofports = {}??
- ? ip_wrapper = ip_lib.IPWrapper()??
- ? ovs = ovs_lib.BaseOVS()??
- ? ovs_bridges = ovs.get_bridges()??
- ? for physical_network, bridge in bridge_mappings.items():??
- ? ?...??
- ? ?if self.use_veth_interconnection:??
- ? ?# enable veth to pass traffic??
- ? int_veth.link.set_up()??
- ? ?phys_veth.link.set_up()??
- ? ?if self.veth_mtu:??
- ? ?# set up mtu size for veth interfaces??
- ? int_veth.link.set_mtu(self.veth_mtu)??
- ? phys_veth.link.set_mtu(self.veth_mtu)??
- ? else:??
- ? # associate patch ports to pass traffic??
- ? self.int_br.set_db_attribute('Interface', int_if_name,??
- ? ?'options', {'peer': phys_if_name})??
- ? br.set_db_attribute('Interface', phys_if_name,??
- ? ? 'options', {'peer': int_if_name})??
虚拟机网卡如何设置 MTU
虚拟机内部网卡配置 MTU 则是通过虚拟机 DHCP 请求 IP 地址时, 顺便请求 MTU 值. 在 RFC2132 DHCP Option and BOOTP Vendor Extensions 里明确定义了 Interface MTU Option.DHCP Option Code 26 用两个字节的 MTU 数据, 定义了网络接口的 MTU 值. 如下表所示.
在 DHCP agent 中, dnsmasq 的 spawn_process 会根据 network 的 MTU 值调整自身的启动参数. 从而使虚拟机在 DHCP 过程中能正确地配置自身网卡的 MTU 值.
- ? class Dnsmasq(DhcpLocalProcess):??
- ? def _build_cmdline_callback(self, pid_file):??
- ? # We ignore local resolv.conf if dns servers are specified??
- ? # or if local resolution is explicitly disabled.??
- ? ...??
- ? mtu = getattr(self.network, 'mtu', 0)??
- ? ?# Do not advertise unknown mtu??
- ? ?if mtu> 0:??
- ? cmd.append('--dhcp-option-force=option:mtu,%d' % mtu)??
- ? ?...??
- ? return cmd??
探测 MTU
通过指定 ICMP 报文内容 size 以及 IP 报文不分片来探测 MTU 值设置是否正确. 注意这里的 size 是指 icmp data size. 该 size 并不包含 ICMP 报文头部长度 (8Byte) 以及 IP 头部长度(20Byte).
Windows 下:
1.? ping -f -l <size> <target_name/target_ip>??
Linux 下:
ping -M do -s <size> <target_name/target_ip>
来源: http://www.bubuko.com/infodetail-3077589.html