Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. 

# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp> 

# All Rights Reserved. 

# 

#    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 2011 VMware, Inc. 

# All Rights Reserved. 

# 

#    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. 

 

import functools 

 

import netaddr 

 

from neutron.agent.common import ovs_lib 

from neutron.plugins.ml2.drivers.openvswitch.agent.common \ 

    import constants 

from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ 

    import br_dvr_process 

from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ 

    import ovs_bridge 

 

 

class OVSTunnelBridge(ovs_bridge.OVSAgentBridge, 

                      br_dvr_process.OVSDVRProcessMixin): 

    """openvswitch agent tunnel bridge specific logic.""" 

 

    # Used by OVSDVRProcessMixin 

    dvr_process_table_id = constants.DVR_PROCESS 

    dvr_process_next_table_id = constants.PATCH_LV_TO_TUN 

 

    def setup_default_table(self, patch_int_ofport, arp_responder_enabled): 

        # Table 0 (default) will sort incoming traffic depending on in_port 

        with self.deferred() as deferred_br: 

            deferred_br.add_flow(priority=1, 

                                 in_port=patch_int_ofport, 

                                 actions="resubmit(,%s)" % 

                                 constants.PATCH_LV_TO_TUN) 

            deferred_br.add_flow(priority=0, actions="drop") 

 

            if arp_responder_enabled: 

                # ARP broadcast-ed request go to the local ARP_RESPONDER 

                # table to be locally resolved 

                # REVISIT(yamamoto): arp_op=arp.ARP_REQUEST 

                deferred_br.add_flow(table=constants.PATCH_LV_TO_TUN, 

                                     priority=1, 

                                     proto='arp', 

                                     dl_dst="ff:ff:ff:ff:ff:ff", 

                                     actions=("resubmit(,%s)" % 

                                       constants.ARP_RESPONDER)) 

 

            # PATCH_LV_TO_TUN table will handle packets coming from patch_int 

            # unicasts go to table UCAST_TO_TUN where remote addresses are 

            # learnt 

            deferred_br.add_flow(table=constants.PATCH_LV_TO_TUN, 

                                 priority=0, 

                                 dl_dst="00:00:00:00:00:00/01:00:00:00:00:00", 

                                 actions=("resubmit(,%s)" % 

                                   constants.UCAST_TO_TUN)) 

 

            # Broadcasts/multicasts go to table FLOOD_TO_TUN that handles 

            # flooding 

            deferred_br.add_flow(table=constants.PATCH_LV_TO_TUN, 

                                 priority=0, 

                                 dl_dst="01:00:00:00:00:00/01:00:00:00:00:00", 

                                 actions=("resubmit(,%s)" % 

                                   constants.FLOOD_TO_TUN)) 

 

            # Tables [tunnel_type]_TUN_TO_LV will set lvid depending on tun_id 

            # for each tunnel type, and resubmit to table LEARN_FROM_TUN where 

            # remote mac addresses will be learnt 

            for tunnel_type in constants.TUNNEL_NETWORK_TYPES: 

                deferred_br.add_flow(table=constants.TUN_TABLE[tunnel_type], 

                                     priority=0, actions="drop") 

 

            # LEARN_FROM_TUN table will have a single flow using a learn action 

            # to dynamically set-up flows in UCAST_TO_TUN corresponding to 

            # remote mac addresses (assumes that lvid has already been set by 

            # a previous flow) 

            learned_flow = ("cookie=%(cookie)s," 

                            "table=%(table)s," 

                            "priority=1," 

                            "hard_timeout=300," 

                            "NXM_OF_VLAN_TCI[0..11]," 

                            "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]," 

                            "load:0->NXM_OF_VLAN_TCI[]," 

                            "load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[]," 

                            "output:NXM_OF_IN_PORT[]" % 

                            {'cookie': self.agent_uuid_stamp, 

                             'table': constants.UCAST_TO_TUN}) 

            # Once remote mac addresses are learnt, output packet to patch_int 

            deferred_br.add_flow(table=constants.LEARN_FROM_TUN, 

                                 priority=1, 

                                 actions="learn(%s),output:%s" % 

                                 (learned_flow, patch_int_ofport)) 

 

            # Egress unicast will be handled in table UCAST_TO_TUN, where 

            # remote mac addresses will be learned. For now, just add a 

            # default flow that will resubmit unknown unicasts to table 

            #  FLOOD_TO_TUN to treat them as broadcasts/multicasts 

            deferred_br.add_flow(table=constants.UCAST_TO_TUN, 

                                 priority=0, 

                                 actions="resubmit(,%s)" % 

                                 constants.FLOOD_TO_TUN) 

 

            if arp_responder_enabled: 

                # If none of the ARP entries correspond to the requested IP, 

                # the broadcast-ed packet is resubmitted to the flooding table 

                deferred_br.add_flow(table=constants.ARP_RESPONDER, 

                                     priority=0, 

                                     actions="resubmit(,%s)" % 

                                     constants.FLOOD_TO_TUN) 

 

        # FLOOD_TO_TUN will handle flooding in tunnels based on lvid, 

        # for now, add a default drop action 

        self.install_drop(table_id=constants.FLOOD_TO_TUN) 

 

    def provision_local_vlan(self, network_type, lvid, segmentation_id, 

                             distributed=False): 

        if distributed: 

            table_id = constants.DVR_NOT_LEARN 

        else: 

            table_id = constants.LEARN_FROM_TUN 

        self.add_flow(table=constants.TUN_TABLE[network_type], 

                      priority=1, 

                      tun_id=segmentation_id, 

                      actions="mod_vlan_vid:%s," 

                      "resubmit(,%s)" % 

                      (lvid, table_id)) 

 

    def reclaim_local_vlan(self, network_type, segmentation_id): 

        self.delete_flows(table=constants.TUN_TABLE[network_type], 

                          tun_id=segmentation_id) 

 

    @staticmethod 

    def _ofport_set_to_str(ports_set): 

        return ",".join(map(str, ports_set)) 

 

    def install_flood_to_tun(self, vlan, tun_id, ports, deferred_br=None): 

        br = deferred_br if deferred_br else self 

        br.mod_flow(table=constants.FLOOD_TO_TUN, 

                    dl_vlan=vlan, 

                    actions="strip_vlan,set_tunnel:%s,output:%s" % 

                    (tun_id, self._ofport_set_to_str(ports))) 

 

    def delete_flood_to_tun(self, vlan, deferred_br=None): 

        br = deferred_br if deferred_br else self 

        br.delete_flows(table=constants.FLOOD_TO_TUN, dl_vlan=vlan) 

 

    def install_unicast_to_tun(self, vlan, tun_id, port, mac, 

                               deferred_br=None): 

        br = deferred_br if deferred_br else self 

        br.add_flow(table=constants.UCAST_TO_TUN, 

                    priority=2, 

                    dl_vlan=vlan, 

                    dl_dst=mac, 

                    actions="strip_vlan,set_tunnel:%s,output:%s" % 

                    (tun_id, port)) 

 

    def delete_unicast_to_tun(self, vlan, mac, deferred_br=None): 

        br = deferred_br if deferred_br else self 

        if mac is None: 

            br.delete_flows(table=constants.UCAST_TO_TUN, 

                            dl_vlan=vlan) 

        else: 

            br.delete_flows(table=constants.UCAST_TO_TUN, 

                            dl_vlan=vlan, 

                            dl_dst=mac) 

 

    def install_arp_responder(self, vlan, ip, mac, deferred_br=None): 

        br = deferred_br if deferred_br else self 

        actions = constants.ARP_RESPONDER_ACTIONS % { 

            'mac': netaddr.EUI(mac, dialect=netaddr.mac_unix), 

            'ip': netaddr.IPAddress(ip), 

        } 

        br.add_flow(table=constants.ARP_RESPONDER, 

                    priority=1, 

                    proto='arp', 

                    dl_vlan=vlan, 

                    nw_dst='%s' % ip, 

                    actions=actions) 

 

    def delete_arp_responder(self, vlan, ip, deferred_br=None): 

        br = deferred_br if deferred_br else self 

        if ip is None: 

            br.delete_flows(table=constants.ARP_RESPONDER, 

                            proto='arp', 

                            dl_vlan=vlan) 

        else: 

            br.delete_flows(table=constants.ARP_RESPONDER, 

                            proto='arp', 

                            dl_vlan=vlan, 

                            nw_dst='%s' % ip) 

 

    def setup_tunnel_port(self, network_type, port, deferred_br=None): 

        br = deferred_br if deferred_br else self 

        br.add_flow(priority=1, 

                    in_port=port, 

                    actions="resubmit(,%s)" % 

                    constants.TUN_TABLE[network_type]) 

 

    def cleanup_tunnel_port(self, port, deferred_br=None): 

        br = deferred_br if deferred_br else self 

        br.delete_flows(in_port=port) 

 

    def add_dvr_mac_tun(self, mac, port): 

        # Table DVR_NOT_LEARN ensures unique dvr macs in the cloud 

        # are not learnt, as they may result in flow explosions 

        self.install_output(table_id=constants.DVR_NOT_LEARN, 

                            priority=1, 

                            eth_src=mac, 

                            port=port) 

 

    def remove_dvr_mac_tun(self, mac): 

        # REVISIT(yamamoto): match in_port as well? 

        self.delete_flows(table_id=constants.DVR_NOT_LEARN, 

                          eth_src=mac) 

 

    def deferred(self): 

        return DeferredOVSTunnelBridge(self) 

 

 

class DeferredOVSTunnelBridge(ovs_lib.DeferredOVSBridge): 

    _METHODS = [ 

        'install_unicast_to_tun', 

        'delete_unicast_to_tun', 

        'install_flood_to_tun', 

        'delete_flood_to_tun', 

        'install_arp_responder', 

        'delete_arp_responder', 

        'setup_tunnel_port', 

        'cleanup_tunnel_port', 

    ] 

 

    def __getattr__(self, name): 

        if name in self._METHODS: 

            m = getattr(self.br, name) 

            return functools.partial(m, deferred_br=self) 

        return super(DeferredOVSTunnelBridge, self).__getattr__(name)