Fork me on GitHub

JS面试题

typeof相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typeof console.log // "function"
typeof NaN // "number"
typeof new Function(); // "function"
typeof new Date(); // "object"
typeof new Boolean(true); // "object"
typeof new Number(1); // "object"
typeof new String("abc"); // "object"
typeof /\*.js/; // "object"
typeof (typeof 1) === 'string'; // typeof返回的肯定是一个字符串
typeof Symbol.iterator; // 'symbol'
typeof undefined === 'undefined'; // true
typeof Math.sin === 'function'; // true
typeof isNaN; // 'function'
typeof isNaN(); // 'boolean'
1
['1', '2', '3'].map(parseInt) // 1, NaN, NaN

总结:
typeof运算符返回7种类型:undefined,string,number,boolean,symbol,object,function
前5个是值类型,后4个是引用类型,typeof只能区分值类型的详细类型,对引用类型无法区分,只能区分function类型。

数值相关

判断下面的值

1
2
3
4
5
6
7
8
parseInt("0a"); // 0
// parseInt("0a")==>parseInt("0")==>parseInt("0",10)==>0
parseInt("010a"); // 8
// parseInt("010a")==>parseInt("010")==>parseInt("10",8)==>8
parseInt("0xt"); // NaN
// parseInt("0xt")==>parseInt("",16)==>NaN

Number.MIN_VALUE > 0 // true

下面代码输出什么

1
2
3
4
5
var two   = 0.2
var one = 0.1
var eight = 0.8
var six = 0.6
[two - one == one, eight - six == two] // [true, false]

表达式的结果是什么

1
2
3
4
5
6
7
8
var a = 111111111111111110000, b = 1111;
a + b;
/*
A: 111111111111111111111
B: 111111111111111110000
C: NaN
D: Infinity
*/

答案:B

解析:
精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。
大于253的数值,都无法保持精度。由于253是一个16位的十进制数值,所以简单的法则就是,JavaScript 对15位的十进制数都可以精确处理。大于253以后,多出来的有效数字(最后三位的111)都会无法保存,变成0。

下面代码输出什么

1
[1 < 2 < 3, 3 < 2 < 1] // [true, true]

运算符相关

下面代码输出什么

1
2
3
var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');
// Something

解析:
+运算符的优先级比三目运算符的优先级高。

下面代码输出什么

1
2
3
4
5
6
7
8
9
10
11
12
function isOdd(num) {
return num % 2 == 1;
}
function isEven(num) {
return num % 2 == 0;
}
function isSane(num) {
return isEven(num) || isOdd(num);
}
var values = [7, 4, '13', -9, Infinity];
values.map(isSane);
// [true, true, true, false, false]

解析:
余数运算符(%)返回前一个运算子被后一个运算子除,所得的余数,运算结果的正负号由第一个运算子的正负号决定,因此-9 % 2 === -1
Infinity % 2返回NaN

下面代码输出什么

1
[] == [] // false

解析:
两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址。

下面代码输出什么

1
2
3
4
5
6
(function(){
var x = y = 1;
})();
console.log(y);
console.log(x);
// 1, error

解析:
赋值运算符的计算顺序是从右开始计算,即右结合。
y是全局变量。

下面代码输出什么

1
2
3
4
5
6
7
8
var a = [1, 2, 3],
b = [1, 2, 3],
c = [1, 2, 4];
a == b
a === b
a > c
a < c
// false, false, false, true

解析:

  • 非相等运算符:如果运算子是对象,会转为原始类型的值,再进行比较。对象转换成原始类型的值,算法是先调用valueOf方法;如果返回的还是对象,再接着调用toString方法。
  • 非相等运算符:两个字符串进行比较时,字符串按照字典顺序进行比较。
  • 相等运算符和严格相等运算符:两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址。
  • 数组的valueOf方法返回数组本身,数组的toString方法返回数组的字符串形式。
  • 对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值。

标准库相关

下面代码输出什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function showCase(value) {
switch(value) {
case 'A':
console.log('Case A');
break;
case 'B':
console.log('Case B');
break;
case undefined:
console.log('undefined');
break;
default:
console.log('Do not know!');
}
}
showCase(new String('A')); // Do not know

解析:
需要注意的是,switch语句后面的表达式,与case语句后面的表示式比较运行结果时,采用的是严格相等运算符(===)。
new String('A')返回的是一个对象。

下面代码输出什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function showCase2(value) {
switch(value) {
case 'A':
console.log('Case A');
break;
case 'B':
console.log('Case B');
break;
case undefined:
console.log('undefined');
break;
default:
console.log('Do not know!');
}
}
showCase2(String('A')); // Case A

解析:
String函数可以将任意类型的值转化成字符串,因此String('A') === 'A' => true

下面代码输出什么

1
2
3
4
var ary = [0,1,2];
ary[10] = 10;
ary.filter(function(x) { return x === undefined;});
// []

解析:
当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位。
数组的某个位置是空位,与某个位置是undefined,是不一样的。如果是空位,使用数组的forEach方法、for...in结构、Object.keys等方法进行遍历,空位都会被跳过。如果某个位置是undefined,遍历的时候就不会被跳过。

作用域相关

下面代码输出什么

1
2
3
4
5
6
7
8
var a = 10;
(function () {
console.log(a)
a = 5
console.log(window.a)
var a = 20;
console.log(a)
})()

依次输出:undefined -> 10 -> 20

解析:
在立即执行函数中,var a = 20;语句定义了一个局部变量a,由于js的变量声明提升机制,局部变量a的声明会被提升至立即执行函数的函数体最上方,且由于这样的提升并不包括赋值,因此第一条打印语句会打印undefined,最后一条语句会打印20。

由于变量声明提升,a = 5;这条语句执行时,局部的变量a已经声明,因此它产生的效果是对局部的变量a赋值,此时window.a依旧是最开始赋值的10。

下面代码输出什么

1
2
3
4
5
6
7
8
9
10
var name = 'Tom';
(function() {
if (typeof name == 'undefined') {
name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
// Hello Tom

解析:
1、首先在进入函数作用域当中,获取name属性
2、在当前作用域没有找到name
3、通过作用域链找到最外层,得到name属性
4、执行else的内容,得到Hello Tom

连续赋值

下面代码输出什么

1
2
3
4
5
6
7
var a = {n: 1};
var b = a;
a.x = a = {n: 2};

console.log(a.x)
console.log(b.x)
// undefined {n: 2}

解析:
1、第3行,.运算符优先级比=高,所以这里首先执行a.x,相当于为a(或者b)所指向的{n: 1}对象新增了一个属性x,即此时对象将变为{n: 1, x: undefined}
2、=从右往左进行赋值,a = {n: 2},此时a的引用已经变成了{n: 2}这个对象,a = {n: 2}这条赋值语句返回{n: 2}
3、执行a.x = {n:2},此时因为a.x已经绑定到了{n: 1 , x: undefined}这个内存地址,也就是b.x,于是{ n: 1, x: undefined} => {n: 1, x: { n: 2}},即b.x = { n: 2 }

Promise

promise输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)

// => 1
// => 2
// => 4
// => 3

输出结果

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
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
}, 0)
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});

}));

first().then((arg) => {
console.log(arg);
});
console.log(4);

// => 3
// => 7
// => 4
// => 1
// => 2
// => 5

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('once')
resolve('success')
}, 1000)
})

const start = Date.now()
promise.then((res) => {
console.log(res, Date.now() - start)
})
promise.then((res) => {
console.log(res, Date.now() - start)
})
/*
once
success 1005
success 1007
*/

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

process.nextTick(() => {
console.log('nextTick')
})
Promise.resolve()
.then(() => {
console.log('then')
})
setImmediate(() => {
console.log('setImmediate')
})
console.log('end')

/*

end
nextTick
then
setImmediate
*/

封装一个异步加载图片的方法

1
2
3
4
5
6
7
8
9
10
11
12
13

function loadImageAsync(url) {
return new Promise(function(resolve,reject) {
var image = new Image();
image.onload = function() {
resolve(image)
};
image.onerror = function() {
reject(new Error('Could not load image at' + url));
};
image.src = url;
});
}

红灯3秒亮一次,绿灯1秒亮一次,黄灯2秒亮一次;如何使用Promise让三个灯不断交替重复亮灯?

题目要求红灯亮过后,绿灯才能亮,绿灯亮过后,黄灯才能亮,黄灯亮过后,红灯才能亮……所以怎么通过Promise实现?

换句话说,就是红灯亮起时,承诺2s秒后亮绿灯,绿灯亮起时承诺1s后亮黄灯,黄灯亮起时,承诺3s后亮红灯……这显然是一个Promise链式调用,看到这里你心里或许就有思路了,我们需要将我们的每一个亮灯动作写在then()方法中,同时返回一个新的Promise,并将其状态由pending设置为fulfilled,允许下一盏灯亮起。

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

function red() {
console.log('red');
}

function green() {
console.log('green');
}

function yellow() {
console.log('yellow');
}


let myLight = (timer, cb) => {
return new Promise((resolve) => {
setTimeout(() => {
cb();
resolve();
}, timer);
});
};


let myStep = () => {
Promise.resolve().then(() => {
return myLight(3000, red);
}).then(() => {
return myLight(2000, green);
}).then(()=>{
return myLight(1000, yellow);
}).then(()=>{
myStep();
})
};
myStep();

// output:
// => red
// => green
// => yellow
// => red
// => green
// => yellow
// => red

输出结果

1
2
3
4
5
6
7
8
9
10

Promise.resolve()
.then(function success (res) {
throw new Error('error')
}, function fail1 (e) {
console.error('fail1: ', e)
})
.catch(function fail2 (e) {
console.error('fail2: ', e)
})

.then可以接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数。

.catch.then第二个参数的简便写法,但是它们用法上有一点需要注意:.then的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,而后续的.catch可以捕获之前的错误。

1
2
3
运行结果:
fail2: Error: error
at success (<anonymous>)

输出结果

1
2
3
4
5
6
7
8
9
10
11

Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})

.then或者.catchreturn一个error对象并不会抛出错误,所以不会被后续的.catch捕获,因为返回任意一个非promise的值都会被包裹成promise对象,即return new Error('error!!!')等价于return Promise.resolve(new Error('error!!!'))

1
2
3
4
运行结果:

then: Error: error!!!
at <anonymous>

反转字符串中的单词

给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例 1:
输入: "Let's take LeetCode contest"
输出: "s'teL ekat edoCteeL tsetnoc"
注意:在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格

1
2
3
4
5
/**
* @param {string} s
* @return {string}
*/
const reverseWords = s => s.split(' ').map(item => item.split('').reverse().join('')).join(' ')

什么是照相机

我们使用 Three.js 创建的场景是三维的,而通常情况下显示屏是二维的,那么三维的场景如何显示到二维的显示屏上呢?照相机就是这样一个抽象,它定义了三维空间到二维屏幕的投影方式,用“照相机”这样一个类比,可以使我们直观地理解这一投影方式。

而针对投影方式的不同,照相机又分为正投影照相机与透视投影照相机。

正投影和透视投影

生活中的物体都是三维的,但是人的眼睛只能看到正面,不能看到被遮挡的背面,三维几何体在人眼睛中的效果就像一张相机拍摄的二维照片,你看到的是一个2D的投影图。 空间几何体转化为一个二维图的过程就是投影,不同的投影方式意味着投影尺寸不同的算法。

对于正投影而言,一条直线放置的角度不同,投影在投影面上面的长短不同;对于透视投影而言,投影的结果除了与几何体的角度有关,还和距离相关,人的眼睛观察世界就是透视投影,比如你观察一条铁路距离越远你会感到两条轨道之间的宽度越小。

无论正投影还是透视投影,three.js 都对相关的投影算法进行了封装, 大家只需要根据不同的应用场景自行选择不同的投影方式。使用OrthographicCamera相机对象的时候,three.js 会按照正投影算法自动计算几何体的投影结果; 使用PerspectiveCamera相机对象的时候,three.js 会按照透视投影算法自动计算几何体的投影结果。

正投影相机对象OrthographicCamera

1
2
3
4
5
6
7
8
9
10
11
/**
* 正投影相机设置
*/
var width = window.innerWidth; //窗口宽度
var height = window.innerHeight; //窗口高度
var k = width / height; //窗口宽高比
var s = 150; //三维场景显示范围控制系数,系数越大,显示的范围越大
//创建相机对象
var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
camera.position.set(200, 300, 200); //设置相机位置
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
1
2
// 构造函数格式
OrthographicCamera( left, right, top, bottom, near, far )
参数(属性) 含义
left 渲染空间的左边界
right 渲染空间的右边界
top 渲染空间的上边界
bottom 渲染空间的下边界
near near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1
far far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值1000

注意

左右边界的距离与上下边界的距离比值与画布的渲染窗口的宽高比例要一致,否则三维模型的显示效果会被单方向不等比例拉伸

构造函数OrthographicCamera的参数( left,right,top,bottom,near,far)本质上是对 WebGL 投影矩阵的封装,宽度width、高度height越大,三维模型顶点的位置坐标就会越大,超出可视区域的网格模型就会被剪裁掉,不会再显示在屏幕上,大家还可以看到leftrighttopbottom互为相反数,这样做的目的是lookAt指向的对象能够显示在canvas画布的中间位置。

为了保持照相机的横竖比例,需要保证(right - left)(top - bottom)的比例与 Canvas 宽度与高度的比例一致。

nearfar都是指到照相机位置在深度平面的位置,而照相机不应该拍摄到其后方的物体,因此这两个值应该均为正值。为了保证场景中的物体不会因为太近或太远而被照相机忽略,一般near的值设置得较小,far的值设置得较大,具体值视场景中物体的位置等决定。

材质(Material)

材质的抽象基类。材质描述了对象的外观。它们的定义方式与渲染器无关。

所有其他材质类型都继承了以下属性和方法(尽管它们可能具有不同的默认值)。

构造函数(Constructor)

1
Material()

该方法创建一个通用材质。

属性(Properties)

属性 参数类型 描述
id Integer 此材质实例的唯一编号。
name String 对象的可选名称(不必是唯一的)。默认值为空字符串。
opacity Float 表明材质的透明度。值0.0表示完全透明,1.0表示完全不透明。如果材质的transparent属性未设置为true,则材质将保持完全不透明,此值仅影响其颜色。默认值为1.0。
transparent Boolean 定义此材质是否透明。这对渲染有影响,因为透明对象需要特殊处理,并在非透明对象之后渲染。设置为true时,通过设置材质的opacity属性来控制材质透明的程度。默认值为false。
visible Boolean 此材质是否可见。默认为true。
side Integer 定义将要渲染哪一面,正面,背面或两者。默认为THREE.FrontSide。其他选项有THREE.BackSide和THREE.DoubleSide。
needsUpdate Boolean 指定需要重新编译材质。如果为true,则会使用新的材质属性刷新它的缓存
blending 决定物体上的材质如何跟背景融合,设置为CustomBlending才能使用自定义blendSrc, blendDst,默认值为NormalBlending。
blendSrc 指定物体如何跟背景融合,默认值为SrcAlphaFactor。
blendDst 定义混合时如何使用背景,默认值为OneMinusSrcAlphaFactor。
blendEquation 使用混合时所采用的混合方程式。默认值为AddEquation,即将两个颜色值相加。
deptTest 是否在渲染此材质时启用深度测试。默认为 true。

常用材质

名称 描述
MeshBasicMaterial 基础网格材质,可以用它赋予几何体一种简单的颜色,或显示几何体的线框
MeshDepthMaterial 深度网格材质,根据网格到相机的距离决定如何给网格染色
MeshNormalMaterial 法线网格材质,一种把法向量映射到RGB颜色的材质。
MeshLambertMaterial Lambert网格材质
MeshPhongMaterial Phong网格材质,一种用于具有镜面高光的光泽表面的材质。
MeshPhysicalMaterial 物理网格材质
LineBasicMaterial 基础线条材质,一种用于绘制线框样式几何体的材质。
LineDashedMaterial 虚线材质,一种用于绘制虚线样式几何体的材质。

基础网格材质(MeshBasicMaterial)

一个以简单着色(平面或线框)方式来绘制几何体的材质。

这种材质不受光照的影响。

1
MeshBasicMaterial( parameters: Object )

属性(Properties)

名称 数据类型 描述
color Color 材质的颜色(Color),默认值为白色 (0xffffff)。
wireframe Boolean 将几何体渲染为线框。默认值为false(即渲染为平面多边形)。
wireframeLinecap String 定义线两端的外观。可选值为 ‘butt’,’round’ 和 ‘square’。默认为’round’。
wireframeLinejoin String 定义线连接节点的样式。可选值为 ‘round’, ‘bevel’ 和 ‘miter’。默认值为 ‘round’。
wireframeLinewidth Float 控制线框宽度。默认值为1。
fog Boolean 材质是否受雾影响。默认为true。
1
var meshMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff});

深度网格材质(MeshDepthMaterial)

一种按深度绘制几何体的材质。深度基于相机远近平面。白色最近,黑色最远。

1
MeshDepthMaterial( parameters: Object )
名称 数据类型 描述
wireframe Boolean 将几何体渲染为线框。默认值为false(即渲染为平面多边形)。
wireframeLinewidth Float 控制线框宽度。默认值为1。

Lambert网格材质(MeshLambertMaterial)

一种非光泽表面的材质,没有镜面高光。

该材质使用基于非物理的Lambertian模型来计算反射率。 这可以很好地模拟一些表面(例如未经处理的木材或石材),但不能模拟具有镜面高光的光泽表面(例如涂漆木材)。

1
MeshLambertMaterial( parameters : Object )
名称 数据类型 描述
color Color 材质的颜色(Color),默认值为白色 (0xffffff)。
wireframe Boolean 将几何体渲染为线框。默认值为false(即渲染为平面多边形)。
wireframeLinecap String 定义线两端的外观。可选值为 ‘butt’,’round’ 和 ‘square’。默认为’round’。
wireframeLinejoin String 定义线连接节点的样式。可选值为 ‘round’, ‘bevel’ 和 ‘miter’。默认值为 ‘round’。
wireframeLinewidth Float 控制线框宽度。默认值为1。
fog Boolean 材质是否受雾影响。默认为true。
emissive Color 材质的放射(光)颜色,基本上是不受其他光照影响的固有颜色。默认为黑色。
aoMapIntensity Float 环境遮挡效果的强度。默认值为1。零是不遮挡效果。
1
var meshMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff})

法线网格材质(MeshNormalMaterial)

名称 名称 描述
BoxGeometry 立方缓冲几何体 BoxGeometry是四边形的原始几何类,它通常使用构造函数所提供的“width”、“height”、“depth”参数来创建立方体或者不规则四边形。
CircleGeometry 圆形缓冲几何体 CircleGeometry是欧式几何的一个简单形状,它由围绕着一个中心点的三角分段的数量所构造,由给定的半径来延展。 同时它也可以用于创建规则多边形,其分段数量取决于该规则多边形的边数。
ConeGeometry 圆锥缓冲几何体 一个用于生成圆锥几何体的类。
CylinderGeometry 圆柱缓冲几何体 一个用于生成圆柱几何体的类。
DodecahedronGeometry 十二面缓冲几何体 一个用于创建十二面几何体的类。
IcosahedronGeometry 二十面缓冲几何体 一个用于生成二十面体的类。
LatheGeometry 车削缓冲几何体 创建具有轴对称性的网格,比如花瓶。车削绕着Y轴来进行旋转。
OctahedronGeometry 八面缓冲几何体 一个用于创建八面体的类。
PlaneGeometry 平面缓冲几何体 一个用于生成平面几何体的类。
RingGeometry 圆环缓冲几何体 一个用于生成二维圆环几何体的类。
ShapeGeometry 形状缓冲几何体 从一个或多个路径形状中创建一个单面多边形几何体。
SphereGeometry 球缓冲几何体 一个用于生成球体的类。
TetrahedronGeometry 四面缓冲几何体 一个用于生成四面几何体的类。
TorusGeometry 圆环缓冲几何体 一个用于生成圆环几何体的类。
TorusKnotGeometry 圆环缓冲扭结几何体 创建一个圆环扭结,其特殊形状由一对互质的整数,p和q所定义。如果p和q不互质,创建出来的几何体将是一个环面链接。
TubeGeometry 管道缓冲几何体 创建一个沿着三维曲线延伸的管道。
ConvexGeometry 凸包几何体 可被用于为传入的一组点生成凸包。
DecalGeometry 贴花几何体 可被用于创建贴花网格物体,以达到不同的目的,例如:为模型增加独特的细节、进行动态的视觉环境改变或覆盖接缝。
TextGeometry 文本缓冲几何体 一个用于将文本生成为单一的几何体的类。
ParametricGeometry 参数化缓冲几何体 生成由参数表示其表面的几何体。
光源名称 描述
AmbientLight 它的颜色会添加到整个场景和所有对象的当前颜色上
PointLight 从一个点向各个方向发射的光源
SportLight 这种光源有聚光的效果,类似台灯、手电筒
DirectionalLight 平行光是沿着特定方向发射的光
HemisphereLight 可以用来创建更加自然的室外光线、模拟反光面和光线微弱的天空

AmbientLight(环境光)

环境光会均匀的照亮场景中的所有物体。环境光会影响整个场景。环境光的光线没有特定的来源,而且这个光源也不会影响阴影的生成,即环境光不能用来投射阴影,因为它没有方向。

不能将环境光作为场景中的唯一光源,需要配合其他光源使用,目的是弱化阴影或添加一些颜色。

创建AmbientLight

1
new THREE.AmbientLight( color: Integer, intensity: Float )

参数说明:

  • color:(参数可选)颜色的rgb数值。缺省值为0xffffff
  • intensity:(参数可选)光照的强度。缺省值为 1。
1
2
3
var ambiColor = "#0c0c0c"
var ambientLight = new THREE.AmbientLight(ambiColor)
scene.add(ambientLight)

PointLight(点光源)

点光源从一个点向各个方向发射的光源。一个常见的例子是模拟一个灯泡发出的光。该光源可以投射阴影。

创建点光源

1
new THREE.PointLight( color: Integer, intensity: Float, distance: Number, decay: Float )

参数说明:

  • color:光源颜色(可选参数),十六进制光照颜色,缺省值0xffffff
  • intensity:光照强度(可选参数),当设置为 0 时,什么都看不到,设成 2,得到的是两倍的亮度,缺省值 1。
  • distance:光源照射的距离,这个距离表示从光源到光照强度为 0 的位置。 当设置为 0 时,光永远不会消失(距离无穷大)。缺省值 0。
  • decay:沿着光照距离的衰退量。缺省值 1。在physically correct模式中,decay = 2
1
2
3
4
var pointColor = "#ccffcc"
var pointLight = new THREE.PointLight(pointColor)
pointLight.distance = 100
scene.add(pointLight)

SpotLight(聚光灯)

光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大。该光源可以投射阴影。

创建聚光灯

1
new THREE.SpotLight( color: Integer, intensity: Float, distance: Float, angle: Radians, penumbra: Float, decay: Float )

参数说明:

  • color:(可选参数)十六进制光照颜色。 缺省值0xffffff
  • intensity:(可选参数)光照强度。 缺省值 1。
  • distance:从光源发出光的最大距离,其强度根据光源的距离线性衰减。
  • angle:光线散射角度,最大为Math.PI/2
  • penumbra:聚光锥的半影衰减百分比。在 0 和 1 之间的值,默认为 0。
  • decay:沿着光照距离的衰减量。
1
2
3
4
5
6
7
8
9
10
11
12
var pointColor = "#ffffff"
var spotLight = new THREE.SpotLight(pointColor)
spotLight.position.set(-40, 60, -10)
spotLight.castShadow = true
spotLight.shadowCameraNear = 2
spotLight.shadowCameraFar = 200
spotLight.shadowCameraFov = 30
spotLight.target = plane
spotLight.distance = 0
spotLight.angle = 0.4

scene.add(spotLight);

DirectionalLight(平行光)

平行光是沿着特定方向发射的光。这种光的表现像是无限远,从它发出的光线都是平行的。常常用平行光来模拟太阳光的效果;太阳足够远,因此我们可以认为太阳的位置是无限远,所以我们认为从太阳发出的光线也都是平行的。平行光可以投射阴影。

创建平行光

1
new THREE.DirectionalLight( color: Integer, intensity: Float )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var pointColor = "#ff5808"
var directionalLight = new THREE.DirectionalLight(pointColor)
directionalLight.position.set(-40, 60, -10)
directionalLight.castShadow = true
directionalLight.shadowCameraNear = 2
directionalLight.shadowCameraFar = 200
directionalLight.shadowCameraLeft = -50
directionalLight.shadowCameraRight = 50
directionalLight.shadowCameraTop = 50
directionalLight.shadowCameraBottom = -50

directionalLight.distance = 0
directionalLight.intensity = 0.5
directionalLight.shadowMapHeight = 1024
directionalLight.shadowMapWidth = 1024

scene.add(directionalLight)

半球光(HemisphereLight)

光源直接放置于场景之上,光照颜色从天空光线颜色渐变到地面光线颜色。半球光不能投射阴影。

创建半球光

1
new THREE.HemisphereLight( skyColor: Integer, groundColor: Integer, intensity: Float )

参数说明:

  • skyColor:(可选参数) 天空中发出光线的颜色。缺省值0xffffff
  • groundColor:(可选参数) 地面发出光线的颜色。缺省值0xffffff
  • intensity:(可选参数) 光照强度。缺省值 1。
1
2
3
var hemiLight = new THREE.HemisphereLight(0x0000ff, 0x00ff00, 0.6)
hemiLight.position.set(0, 500, 0)
scene.add(hemiLight)

平面光光源(RectAreaLight)

平面光光源从一个矩形平面上均匀地发射光线。这种光源可以用来模拟像明亮的窗户或者条状灯光光源。

注意事项:

  • 不支持阴影。
  • 只支持MeshStandardMaterialMeshPhysicalMaterial两种材质。
  • 你必须在你的场景中加入RectAreaLightUniformsLib,并调用init()

创建平面光

1
RectAreaLight( color : Integer, intensity : Float, width : Float, height : Float )

参数说明:

  • color:(可选参数) 十六进制数字表示的光照颜色。缺省值为0xffffff
  • intensity:(可选参数) 光源强度/亮度。缺省值为 1。
  • width:(可选参数) 光源宽度。缺省值为 10。
  • height:(可选参数) 光源高度。缺省值为 10。
1
2
3
4
5
6
var areaLight = new THREE.RectAreaLight(0xff0000, 3)
areaLight.position.set(-10, 10, -35)
areaLight.rotation.set(-Math.PI / 2, 0, 0)
areaLight.width = 4
areaLight.height = 9.9
scene.add(areaLight)

防火墙

防火墙是一种安全设备,保护一个网络区域免受另一个网络区域的攻击和入侵,通常被部署在网络边界,例如:企业互联网出口。

简单讲防火墙作为网络中的设备,它的作用也是对网络起到安全保护的作用,对进出网络的流量进行安全管理,保证内网的安全性。

防火墙分类

按照硬件形态,防火墙可以分为盒式防火墙、框式防护墙。

按照软硬件区分:防火墙可以分为软件防火墙和硬件防火墙。软件防火墙又可分为个人防火墙和网关防火墙。

按照防火墙技术原理:防火墙可以分为包过滤防火墙、状态检测防火墙,AI 防火墙。

软件防火墙

个人防火墙

个人防火墙运行在 PC 上,用于监控 PC 和外网的通信信息。在 Windows 操作系统中集成了 Windows 防火墙。

杀毒软件产品厂家的个人防火墙一般包含在安全软件套件里。

个人防火墙产品的功能 说明
确认连接请求 向用户确认是否阻止特定的连接请求
安全日志 根据需要生成安全日志,记录 PC 正常连接与错误连接的信息。在做故障分析时,日志起到了很大的作用
反病毒功能 阻止 PC 接收病毒和蠕虫信息
反间谍功能 阻止 PC 接收间谍软件或程序的通信
个人信息保护功能 设置保护策略,防止个人信息被窃取、浏览恶意网站以及钓鱼诈骗等

网关防火墙

在网络中的网关上配置防火墙的功能,能对网络中的流量进行策略控制,这就是网关防火墙。

网关防火墙分为两种,一种是在 Windows、Linux 等操作系统上安装并运行防火墙软件的软件网关防火墙,另一种是使用专用设备的硬件网关防火墙。

个人防火墙主要监控 PC 的通信流量,网关防火墙是监控网络中所有终端的通信流量,在网关处进行策略控制。

区别 个人防火墙 网关防火墙
安装位置 用户的 PC 上 Windows或Linux等服务器上
网络中的位置 终端 网关
安装监控对象 流入终端的流量 经过网关的流量
加密通信 在终端上解密后检查 不能检查
压缩文件检查 解压后检查 对解压方式、解压基本有限制
口令文件检查 输入口令后解压检查 不能检查

硬件防火墙

通过硬件设备实现的防火墙叫做硬件防火墙,外形跟路由器相似,接口类型通常有千兆网口、万兆光口。

防火墙技术类型

防火墙在网络边界判断允许进行的通信和不允许的通信作为其判断依据的技术类型的演进:

  • 分组过滤型:没有防火墙设备时,可以由路由器实现。根据网络中的传输的 IP 分组头部和 TCP/IP 分组头部,获得源 IP 地址和端口号、目的 IP 地址和端口号,以这些信息作为过滤条件,决定是否转发这个分组。分组过滤会用到访问控制列表。
  • 应用网关型:不以分组为单位进行通信过滤,而是特定的应用程序会话。
  • 电路层网关型:在传输层进行连接中继,也就是第四层代理,通过 SOCKS 协议实现。内网终端连接外部网络时,终端与网关建立 TCP 连接,网关和外部服务器建立新的 TCP 连接。使用电路层网关,不用设置安全认证端口和 NAT,就可以让私有地址的内网终端访问外部网络。
  • 状态检测型:动态分组过滤的一种,通过检测 TCP 的连接状态阻挡来路不明的分组,简称 SPI。可以抵抗下面类型的攻击:伪装 IP 地址或端口,发送附带 TCP 的 RST 或 FIN 标志位的分组,随意终止正常通信的攻击;在允许通信的范围内发送附带 TCP 的 ACK 标志位的分组,从而入侵内部网络;在 FTP 通信时,无论是否建立控制连接,都会创建数据连接进而入侵内部网络
  • 新一代防火墙:根据上面的所有信息执行安全策略来进行防御,不仅根据端口号或协议号识别应用程序,还根据 IP 地址识别用户信息

代理服务器

代理服务器是应用网关防火墙的一种。假设客户端和 HTTP 服务器通信时,客户端发送请求报文时,代理服务器会替代客户端向 HTTP 服务器发送请求;HTTP 服务器回复响应报文时,代理服务器会代替 HTTP 服务器向客户端回复。对于客户端来说,代理服务器就是 HTTP 服务器。客户端和代理服务器、代理服务器和 HTTP 服务器分别建立两个会话。

从客户端收到的请求报文、从服务器收到响应报文,代理服务器都会在应用层进行检查,如果有异常就放弃通信或发送出错信息。

由于代理服务器是会话的起点,对互联网的服务器来说,是看不到客户端的 IP 地址。

报文过滤防火墙是以 IP 或 TCP/UDP 为对象,判断是否允许通信。而应用网关防火墙是以应用程序为对象,也就是将 FTP、HTTP、Telnet、DNS 等为对象进行判断。

防火墙的接口模式

防火墙有四种接口模式,分别是 L3 模式、L2 模式、L1 模式和 TAP 模式。

接口模式 说明
L3 模式 也叫做 NAT 模式,和路由器接口一样,是拥有 IP 地址的接口。使用路由选择、NAT 以及连接 IPSec VPN 或 SSL VPN 时,必须使用 L3 模式接口。接口可配置静态 IP 地址,也可通过 PPPoE、DHCP动态获取 IP 地址
L2 模式 也叫做透传模式或透明模式,和交换机一样,是进行交接的接口。进行 IP 地址分配时,需要使用 VLAN
L1 模式 也叫做虚拟线缆模式。把两个接口组成一组,流量在一个接口输入,在另一个接口输出。这种模式下无法进行路由和桥接
TAP 模式 与交换机镜像端口连接的模式。对交换机的数据帧进行检测。由于不是串联,无法阻止非法通信

L1 ~ L3 模式是将防火墙进行串连,TAP 模式是防火墙进行旁挂。

防火墙能防范的威胁

  • 窃听:通过窃听网络数据获取银行卡号、密码等重要信息
  • 篡改:将网站主页、邮件等通信内容恶意修改
  • 破坏:通过电脑病毒或DoS攻击等破坏系统的正常工作
  • 冒充:冒充他人发送邮件,对接收方进行钓鱼、诈骗等行为
  • 信息泄露:电脑或服务器上的重要信息或文档泄露
  • 攻击跳板:作为病毒部署或DoS攻击的跳板
  • 垃圾邮件:以营利为目的发送大量邮件

防火墙功能

防火墙常见的功能有:会话管理、报文结构解析、安全区域、安全策略、NAT、VPN、DoS 防御、报文攻击防御、内容扫描、监控和报告、报文抓包。

会话管理

会话是两个终端系统之间的逻辑连接,从开始到结束的通信过程。

在 TCP 中,客户端和服务器通信,使用 3 次握手建立 1 个 TCP 连接,客户端发送请求( request ),服务器进行回应( response ),直至结束的过程就是进行了 1 个会话通信。

在 UDP 中,客户端和服务器的源端口和目的端口一致,之后的一系列通信都叫做会话。

在 ICMP 中,Echo request 和对应的 Echo reply 组成 1 个会话。

数据流是一组有序,有起点和终点的数据序列。一个会话有两个数据流( flow ):一个是 “ 客户端到服务器 ”( client to server ),另一个是 “ 服务器到客户端 ”( server to client )。

TCP 连接管理

在数据通信前,客户端发送一个 SYN 包作为建立连接的请求。如果服务器发来回应,则认为可以开始数据通信。如果未收到服务器的回应,就不会进行数据通信。在通信结束时,会使用 FIN 包进行断开连接的处理。

SYN 包和 FIN 包是通过 TCP 头部的控制字段来管理 TCP 连接。一个连接的建立与断开,正常过程至少需要来回发送 7 个包才能完成。建立一个 TCP 连接需要发送 3 个包,这个过程叫作三次握手。断开一个 TCP 连接需要发送 4 个包,这个过程也称作四次挥手。创建一个 TCP 连接,会产生一个 32 位随机序列号,因为每一个新的连接使用一个新的随机序列号。

  • SYN 检查
    TCP 会话开始时,客户端会发送一个 SYN 消息。如果没有会话信息,或尚未建立会话,即非 SYN 消息的 TCP 数据段到达防火墙,防火墙会当做非法消息而丢弃。
  • ACK 检查
    通过对 SYN-ACK 的 ACK 消息检查,确认进行中的 3 次握手是否是非法尝试,防范 SYN Flood 攻击。
  • 重复数据段检查
    防火墙收到重复数据段,也就是序列号相同的 TCP 数据段,可以选择接收或者丢弃。
  • 窗口检查
    防火墙可以检测 TCP 头部的序列号和滑动窗口大小,拦截超过滑动窗口容量数据的序列号。
  • 数据段重组
    防火墙可以验证 TCP 数据段序列号是否完整。

会话建立的处理

  1. 防火墙收到报文后,首先检查会话表,确认是否有相同的会话。如果有相同会话,那么会禁止会话建立,确保会话都是唯一的。
  2. 如果是不同会话,那么检查报文,通常是查看路由表或 MAC 地址表来确定转发路径。如果可以转发,就确定对应的转发出接口和目的网段。如果不能转发,就丢弃这个数据。
  3. 报文检查目的地址是否需要进行 NAT。如果需要,就先完成 NAT,然后转发到相应出接口和目的网段。
  4. 对报文和目的信息进行安全策略检查,源信息是源接口、源区域和源地址,目的信息是目的接口、目的区域和目的地址。如果有匹配的安全策略,就根据策略进行处理,允许通信就进行转发,拒绝通信就进行丢弃。如果没有匹配的安全策略,就根据默认拒绝的策略丢弃数据。
  5. 当报文被允许通信时,防火墙的会话表中就会生成相应的会话信息。

会话的生存时间

自动生成的会话表信息,是有一定的生存时间。会话建立后,一段时间内一直没有进行通信,防火墙会删除生存时间到期的会话表项。如果长期保留会话表项,这些会话信息可能会被恶意攻击。同时,会话表是会占用防火墙资源,防火墙的会话表项的数量也是有限的,长期保留闲置的会话,会影响新会话的生成。

会话时间可以根据协议的不同,分别进行设置。

TCP 的话,会话的超时时间通常是 30 分钟到 1 小时,UDP 是 30 秒。比如,Telnet 连接在防火墙上建立会话后,如果在 1 个小时内没有任何数据通信,防火墙会自动删除这个会话表项。客户端无法再次使用这个 Telnet 会话了。

会话终止处理

客户端完成数据传输后,发送 FIN 消息,即使用 FIN 标志位的 TCP 数据段。

服务器收到 FIN 消息后,在回复消息中,使用 FIN 和 ACK 标志位,并将 ACK 编号设置为“接收的 Seq 编号 + 1 ” 。

客户端相同处理方式,在回复消息中,使用 ACK 标志位,并将 ACK 编号设置为“接收的 Seq 编号 + 1 ” 。

如果客户端或服务器在连接过程发生故障,只有一方是侦听状态,这叫做半侦听或半关闭。如果通信恢复,接收到故障前的数据段,那么会回复 RST 消息,强制终止 TCP 连接。

当防火墙收到 FIN 或 RST 消息时,会启动一个 30 秒的定时器。即使 FIN → FIN-ACK → ACK 的终止过程没完成,防火墙也会强制删除会话表项。

UDP 数据流的管理

UDP 不需要像 TCP 一样 3 次握手,客户端和服务器直接使用应用程序的 UDP 数据进行交互。

UDP 数据流是指源 IP 地址、源端口号、目的 IP 地址和目的端口号这 4 个参数都相同的一系列 UDP 数据。

DNS 和 SNMP 这类应用程序,只需要 1 个 UDP 数据,就能构成 1 个数据流。

音频和视频使用的 RTP,就需要多个 UDP 数据,来构成 1 个数据流。

管理 ICMP 和 IP 数据流

像 ICMP 这类没有端口号的协议,是直接根据 IP 头部的协议号来生成会话。

防火墙通过识别 ICMP 不同的请求消息和对应的响应消息,来判断这些消息序列是否属于同一个会话。

会话同步

通常两台防火墙会使用主备方式的冗余结构,对主防火墙和备防火墙的会话信息进行同步。主防火墙负责建立用户通信的会话,并把会话信息记录到会话表中,同时将信息转发到备防火墙。

会话管理有什么防御功能?

防火墙可以通过限制会话数量,能够防范 DoS 攻击,还能控制防火墙的负载,提高防火墙的性能。

防火墙可以以 TCP SYN 、UDP 、ICMP 等协议为单位,通过指定源与目的的组合方式,来限制这类会话的数目。

分组结构解析

为了防止非法报文的流入和流出,防火墙会对报文的头部和数据进行解析。常见的有:IP 头部解析、TCP 头部解析、UDP 头部解析。

IP 头部解析

数据帧和 IPv4 头部的解析内容如下:

以太网类型与 IP 版本:以太网数据帧头部的类型字段为 0x0800 时表示 IPv4 ,同时 IPv4 头部的版本也是 4 。类型字段为 0x86DD 时表示 IPv6 ,IP 头部的版本也是 6 。

IP 头部:确认数据是否完整,并检查报文长度与实际长度是否一致。

IP 协议号、TTL :检查字段值,如果值为 0 就丢弃报文。

源地址、目的地址:确认是否存在 LAND attack 。

数据总长度:确认是否存在 ping of death 攻击。

标志位、分片偏移:丢弃无法进行分片的报文。

可选项:丢弃无用可选项的报文。

TCP 头部解析

TCP 头部的解析内容如下:

TCP 头部:确认各个字段是否完整、是否有被中途截断。

数据偏移:确认数据偏移字段的值是否是 5 以下,TCP 头部长度最小是 5 字符 = 20 字节。

校验和:确认校验和是否错误。

端口号:确认源端口号和目的端口号是否为 0 。

控制位:检查 SYN 、ACK 等字段是否存在组合不正确的情况。

UDP 头部解析

UDP 头部的解析内容如下:

UDP 头部:确认各个字段是否完整、是否有被中途截断。

校验和:确认校验和是否错误。

安全区域

防火墙有安全区域的概念。防火墙的物理接口和逻辑接口会分配到不同的区域中,也就是将防火墙的网段分别划分到不同的区域中。一个网络接口只能属于一个区域。

在同一个区域内,可以自由进行通信,但是跨区域通信,必须符合安全策略才行。当然,防火墙也可以设置安全策略,根据源或目的地址等条件,判断在同一区域内能否允许通信。

一个安全区域可以说就是若干个接口的集合,一个安全区域里面的接口具有相同的安全属性。

如下图所示:防火墙把不同的接口分成3个安全区域,出口区为untrust区域,内网区分为trust区和DMZ区。

默认安全区域

防火墙划分了 4 个默认的安全区域:

安全区域 说明 优先级
受信区域(trust 通常将内网终端用户所在区域划分为trust区域 85
非受信区域(untrust 通常将Internet等不安全的网络划分为untrust区域 5
非军事化区域(dmz 通常将内网服务器所在区域划分为DMZ区域 50
本地区域(local 设备本身,包括设备的各接口本身 100

默认的安全区域不能够删除,每个安全区域都设置了固定的优先级。优先级值越大,表示优先级越高。

除了这四个域之外,用户也可以根据自己的需求创建自定义域。自定义的域的优先级是可以自己调节的。

安全策略

防火墙的主要功能是访问控制,也就是判断特定源和特定目的之间是否允许进行特定的通信。访问控制是通过规则来实现,每一条规则都指定了源、目的和通信内容等信息。这些访问控制规则的集合,在路由器中,叫做访问控制列表,而在防火墙中,叫做安全策略或安全规则。

什么是安全策略

安全策略是防火墙中对流量转发、以及对流量中的内容进行安全一体化检测的策略。

当防火墙收到流量后,会对流量的属性(包括五元组、用户、时间段等)进行识别,从而和安全策略进行匹配,如果能够匹配上,则执行相应的动作。

如上图所示:PC 访问internet,匹配到防火墙安全策略,动作为permit,因此流量可以通过防火墙。如果动作为deny,则流量不能够通过防火墙。

防火墙的安全策略

防火墙的安全策略以区域作为对象,还可以以应用程序名称和用户名称等信息作为对象。

举个栗子:在上图的安全策略中,192.168.2.1从信任区域向不信任区域的 80 端口通信时,防火墙首先执行第 1 条安全策略,发现源地址不匹配,不执行Allow。接着执行第 2 条安全策略,发现地址和端口匹配,执行Deny,也就是拒绝通信。防火墙的安全策略从上往下依次执行的行为,也叫做安全策略查找(policy lookup)。

Any表示任何值都与策略匹配。如果是安全策略中,出现未定义的通信,比如从信任区到 DMZ 区域的通信,防火墙默认执行拒绝,这个策略叫做 “ 默认拒绝 ”(implicit deny)。

如果需要在防火墙没有匹配的情况下,执行Allow,可以在安全策略的最后一行设置对象为Any,行为为Allow的策略。

当然,防火墙的安全策略是会有上限,上限由产品规格决定。而且当表项越多时,设备性能也会随之下降。

安全策略组成

安全策略由匹配条件、动作、安全配置文件组成。

  1. 匹配条件:
    匹配条件包括五元组(源地址、目的地址、源端口、目的端口、协议)、VLAN、源安全区域、目的安全区域、用户、时间段等。
  2. 动作:动作包括允许和禁止。 如果动作为“允许”:
  • 如果没有配置内容安全检测,则允许流量通过。
  • 如果配置内容安全检测,最终根据内容安全检测的结论来判断是否对流量进行放行。
    禁止:表示拒绝符合条件的流量通过。
  • 如果动作为“禁止”,防火墙不仅可以将报文丢弃;
  • 还可以针对不同的报文类型选择发送对应的反馈报文。发起连接请求的客户端/服务器收到防火墙发送的阻断报文后,可以快速结束会话并让用户感知到请求被阻断。
  1. 安全配置文件:内容安全检测包括反病毒、入侵防御等,它是通过在安全策略中引用安全配置文件实现的。
  • 如果其中一个安全配置文件阻断该流量,则防火墙阻断该流量。
  • 如果所有的安全配置文件都允许该流量转发,则防火墙允许该流量转发。

安全策略匹配过程

防火墙的安全策略一般配置很多条,如果都可以匹配应该优先匹配哪一条呢?

安全策略的匹配按照策略列表顺序执行,从上往下逐条匹配,如果匹配了某条策略,将不再往下匹配。

因此,配置安全策略的顺序很重要,需要优先配置精确的安全策略,然后再配置粗略的安全策略。

系统默认存在一条缺省安全策略default

缺省安全策略位于策略列表的最底部,优先级最低,所有匹配条件均为any,动作默认为禁止。如果所有配置的策略都未匹配,则将匹配缺省安全策略default

会话表

会话表用来记录 TCP、UDP、ICMP 等协议连接状态的表项,是防火墙转发报文的重要依据;

那么什么是基于连接状态转发报文呢?防火墙基于“状态”转发报文:

  1. 只对首包或者少量报文进行检测然后确认一个连接状态;(会话表)
  2. 后续大量的报文根据连接状态进行控制;

会话表就记录了大量的连接状态;这种机制大大的提升了防火墙的检测和转发效率。

如上图所示:客户端 PC1 访问服务器 PC2,PC1 向 PC2 发起 HTTP 连接;

  1. PC1发送报文;
  2. 首包达到防火墙,创建会话表项(如下);防火墙会话表中标示出http协议和连接信息,并识别出此流量在公共路由表中被转发;
    1
    http VPN: public->public 192.168.1.254:10000->10.10.1.254:80
  3. 防火墙放行报文;
  4. PC2 回复报文;
  5. 回复报文匹配防火墙会话;
  6. 防火墙转发报文;

会话表的创建

防火墙在开启状态检测情况下,只有首包会创建会话表项,后续报文匹配会话表即可转发。

会话表老化时间

对于一个已经建立的会话表表项,只有当它不断被报文匹配才有存在的必要。如果长时间没有报文匹配,则说明可能通信双方已经断开了连接,不再需要该条会话表项了。

为了节约系统资源,系统会在一条表项连续未被匹配一段时间后,将其删除,即会话表项已经老化。

长连接

对于某些特殊业务中,一条会话的两个连续报文可能间隔时间很长。

例如:

  • 用户通过 FTP 下载大文件,需要间隔很长时间才会在控制通道继续发送控制报文。
  • 用户需要查询数据库服务器上的数据,这些查询操作的时间间隔远大于 TCP 的会话老化时间。

如果只靠延长这些业务所属协议的老化时间来解决这个问题,会导致一些同样属于这个协议,但是其实并不需要这么长的老化时间的会话长时间不能得到老化。

这会导致系统资源被大量占用,性能下降,甚至无法再为其他业务建立会话。所以必须缩小延长老化时间的流量范围。

长连接功能可以解决这一问题。长连接功能可以为这些特殊流量设定超长的老化时间。

Server-map

为什么会出现server-map表?

由于会话表对哪些报文属于同一条流量的标准过于严格,会导致一些特殊协议不能正确匹配会话表。

Server-map表可以解决这一问题。

例如使用 FTP 协议的port方式传输文件时:

  • 既需要客户端主动向服务器端发起控制连接;
  • 又需要服务器端主动向客户端发起服务器数据连接;

如果设备上配置的安全策略为允许单方向上报文主动通过,则 FTP 文件传输不能成功。

server-map表原理

通常情况下,如果在设备上配置严格的安全策略,那么设备将只允许内网用户单方向主动访问外网。

为了解决这一类问题,防火墙引入了Server-map表,Server-map用于存放一种映射关系。

  • 这种映射关系可以是控制数据协商出来的数据连接关系;
  • 也可以是配置 NAT 中的地址映射关系;

使得外部网络能透过设备主动访问内部网络。

生成Server-map表之后,如果一个数据连接匹配了Server-map表项,那么就能够被设备正常转发,并在匹配Server-map表后创建会话,保证后续报文能够按照会话表转发。

server-map表报文转发过程

防火墙收到报文后,如果没有命中会话表,防火墙则进入首包处理流程,查询是否命中server-map表。

  • 如果命中,则生成会话表,转发报文;
  • 如果没有命中,则执行其他包处理流程。

防火墙应用场景

1、企业边界防护

如下图所示,企业内网业务部署在trust区,服务器部署在 DMZ。

  • 企业内网访问internet时经过防火墙,防火墙控制内外网流量,进行安全控制;
  • 外网用户访问服务器时经过防火墙,对内网服务器进行保护;

2、内网安全隔离

如下图所示:公司分为市场部、生产部,财经部,研发部,不同部分之间互访经过防火墙。通过防火墙进行安全控制。

3、数据中心边界防护

数据中心网络访问internet时,需要经过防火墙进行安全控制,对内网业务进行安全保护。

二层交换机

二层交换机的特点

  • 二层交换机可以充当网桥,将计算机网络系统的各种终端设备连接在一个平台上。它们能够非常快速且有效地将数据从LAN 网络的源端传输到目标端。
  • 二层交换机通过从交换机的地址表中学习目的节点的 MAC 地址,执行交换功能,将数据帧从源端重新排列到目的端。
  • MAC地址表为二层设备提供了唯一的地址,用于标识数据下发的终端设备和节点。
  • 二层交换机将庞大复杂的 LAN 网络拆分为一个个小的 VLAN 网络。
  • 通过在一个大型的 LAN 网络中配置多个 VLAN,在没有物理连接的情况下,交换变得更快。

二层交换机的应用

  • 通过二层交换机,我们可以轻松地将位于同一 VLAN 内的数据帧从源端发送到目的端,而无需物理连接或位于同一位置。
  • 因此,软件公司的服务器可以集中放置在一个位置,而分散在其他位置的客户端可以轻松访问数据而没有延迟,从而节省服务器成本和时间。
  • 组织可以通过使用这些类型的交换机将主机配置在同一个 VLAN 中,而不需要任何互联网连接,从而实现内部通信。

交换机二层转发原理

交换机有多个网络端口,它通过识别数据帧的目标 MAC 地址,根据 MAC 地址表决定从哪个端口发送数据。MAC 地址表不需要在交换机上手工设置,而是可以自动生成的。

交换机是如何添加、更新、删除 MAC 地址表条目的?

在初始状态下,交换机的 MAC 地址表是空的,不包含任何条目。当交换机的某个端口接收到一个数据帧时,它就会将这个数据帧的源 MAC 地址、接收数据帧的端口号作为一个条目保存在自己的 MAC 地址表中,同时在接收到这个数据帧时重置这个条目的老化计时器时间。这就是交换机自动添加 MAC 地址表条目的方式。

在新增这一条 MAC 地址条目后,如果交换机再次从同一个端口收到相同 MAC 地址为源 MAC 地址的数据帧时,交换机就会更新这个条目的老化计时器,确保活跃的的条目不会老化。但是如果在老化时间内都没收到匹配这个条目的数据帧,交换机就会将这个老化的条目从自己的 MAC 地址表中删除。

还可以手动在交换机的 MAC 地址表中添加静态条目。静态添加的 MAC 地址条目优先动态学习的条目进行转发,而且静态条目没有老化时间,会一直保存在交换机的 MAC 地址表中。

如何使用 MAC 地址表条目进行转发?

当交换机的某个端口收到一个单播数据帧时,它会查看这个数据帧的二层头部信息,并进行两个操作。一个操作是根据源 MAC 地址和端口信息添加或更新 MAC 地址表。另一个操作是查看数据帧的目的 MAC 地址,并根据数据帧的目的 MAC 地址查找自己的 MAC 地址表。在查找 MAC 地址表后,交换机会根据查找结果对数据帧进行处理,这里有 3 中情况:

  1. 交换机没有在 MAC 地址表中找到这个数据帧的目的 MAC 地址,因此交换机不知道自己的端口是否有连接这个 MAC 地址的设备。于是,交换机将这个数据帧从除了接收端口之外的所有端口泛洪出去。
  2. 交换机的 MAC 地址表中有这个数据帧的目的 MAC 地址,且对应端口不是接收到这个数据帧的端口,交换机知道目的设备连接在哪个端口上,因此交换机会根据 MAC 地址表中的条目将数据帧从对应端口单播转发出去,而其它与交换机相连的设备则不会收到这个数据帧。
  3. 交换机的 MAC 地址表中有这个数据帧的目的 MAC 地址,且对应端口就是接收到这个数据帧的端口。这种情况下,交换机会认为数据帧的目的地址就在这个端口所连接的范围内,因此目的设备应该已经收到数据帧。这个数据帧与其它端口的设备无关,不会将数据帧从其它端口转发出去。于是,交换机会丢弃数据帧。

单播:主机一对一的发送数据。单播地址是主机的 MAC 地址。

广播:向局域网内所有设备发送数据。只有全 1 的 MAC 地址为广播 MAC 地址,即FF-FF-FF-FF-FF-FF

泛洪:将某个端口收到的数据从除该端口之外的所有端口发送出去。泛洪操作广播的是普通数据帧而不是广播帧。

VLAN

冲突和广播域

在二层交换中,当两个或多个主机试图在同一网络链路上以相同的时间间隔进行通信时,可能会发生冲突。当数据帧发生冲突,设备必须重新发送数据。冲突对网络性能有严重的负面影响,因此绝对要避免冲突。

广播是一种信息的传播方式,指网络中的某一设备同时向网络中所有的其他设备发送数据,这个数据所能广播到的范围即为广播域。简单点说,广播域就是指网络中所有能接收到同样广播消息的设备的集合。

使用一个或多个交换机组成的以太网,所有站点都在同一个广播域。随着交换机变多,这个广播域的范围也会变大,于是就会出现难以维护、广播风暴以及安全等问题。

一个主机想要获取另外一个网段的主机 MAC 地址,需要发送 ARP 广播请求获取对方主机的 MAC 地址。这个广播请求会广播到每一个主机身上,容易导致广播风暴。

在一个广播域内的任意两台主机之间可以任意通信,通信数据有被窃取的风险。

为了解决广播域扩大带来的性能问题和安全性降低问题, VLAN 技术应运而生。 VLAN 技术能够在逻辑上把一个物理局域网分隔为多个广播域,每个广播域称为一个虚拟局域网(即 VLAN)。每台主机只能属于一个 VLAN,同属一个 VLAN 的主机通过二层直接通信,属于不同 VLAN 的主机只能通过 IP 路由功能才能实现通信。通过划分多个 VLAN,从而减小广播域传播的范围,过滤多余的包,提高网络的传输效率,同时提高了网络的安全性。

VLAN 原理

VLAN 技术通过给数据帧插入 VLAN 标签(又叫 VLAN TAG)的方式,让交换机能够分辨出各个数据帧所属的 VLAN。

VLAN 标签是用来区分数据帧所属 VLAN 的,是 4 个字节长度的字段,插入到以太网帧头部上。VLAN 标签会插入到源 MAC 地址后面,IEEE 802.1Q 标准有这个格式定义和字段构成说明。

  • TPID(标签协议标识符):长度 2 个字节,值为0x8100,用来表示这个数据帧携带了 802.1Q 标签。不支持 802.1Q 标准的设备收到这类数据帧,会把它丢弃。
  • TCI(标签控制信息):长度 2 个字节,又分为三个子字段,用来表示数据帧的控制信息:
  • 优先级:长度为 3 比特,取值范围 0 ~ 7,用来表示数据帧的优先级。取值越大,优先级越高。当交换机发送拥塞是,优先转发优先级高的数据帧。
  • CFI(规范格式指示器):长度为 1 比特,取值非 0 即 1。
  • VLAN ID(VLAN 标识符):长度为 12 比特,用来表示 VLAN 标签的数值。取值范围是 1 ~ 4094。

划分 VLAN 后,交换机如何处理广播报文?

交换机上划分了多个 VLAN 时,在交换机接收到广播数据帧时,只会将这个数据帧在相同 VLAN 的端口进行广播。

划分 VLAN 后,交换机如何处理目的 MAC 地址不在 MAC 地址表中的单播数据帧?

交换机上划分了多个 VLAN 时,当交换机接收到一个目的 MAC 地址不存在于自己 MAC 地址表中的单播数据帧时,只会将这个数据帧在相同 VLAN 的端口进行泛洪。

划分 VLAN 后,不同 VLAN 的主机能否通信?

划分多 VLAN 的环境中,即使交换机 MAC 地址表里保存了某个数据帧的目的 MAC 地址条目,若这个目的 MAC 地址所对应的端口与数据帧的入端口在不同的 VLAN 中,交换机也不会通过 MAC 地址表中的端口发送数据帧。

小结:在不使用路由转发的前提下,交换机不会从一个 VLAN 的端口中接收到的数据帧,转发给其它 VLAN 的端口。

怎么区分不同的 VLAN ?

通过 VLAN ID 进行区分,例如 VLAN 10 和 VLAN 20 就是不同的 VLAN。

VLAN 技术好处

  • 增加了广播域的数量,减小了每个广播域的规模,也减少了每个广播域中终端设备的数量;
  • 增强了网络安全性,保障网络安全的方法增加了;
  • 提高了网络设计的逻辑性,可以规避地理、物理等因素对于网络设计的限制。

划分 VLAN

我们可以使用不同的方法,把交换机上的每个端口划分到某个 VLAN 中,以此在逻辑上分隔广播域。

交换机通常会使用基于端口划分 VLAN 的方法。在交换机上手动配置,绑定交换机端口和 VLAN ID 的关系。

优点:配置简单。想要把某个端口划分到某个 VLAN 中,只需要把端口的 PVID (端口 VLAN ID)配置到相应的 VLAN ID 即可。

缺点:当终端设备移动位置是,可能需要为终端设备连接的新端口重新划分 VLAN。

除了这种方法外,还可以使用基于 MAC 地址划分 VLAN、基于 IP 地址划分 VLAN、基于协议划分 VLAN、基于策略划分 VLAN 等方法来划分 VLAN。

PVID:接口默认 VLAN ID,是交换机端口配置的参数,默认值是 1。

跨交换机 VLAN 原理

终端设备不会生成带 VLAN 标签的数据帧,它们发出的数据帧叫做无标记帧(Untagged)。它们连接的交换机会给无标记帧打上 VLAN 标签。交换机通过每个端口的 PVID,判断从这个接口收到的无标记帧属于哪个 VLAN,并在转发时,插入相应的 VLAN 标签,从而将无标记帧变为标记帧(Tagged)。

当两台交换机通过端口连接时,收到的数据帧是标记帧还是无标记帧?交换机端口会如何处理呢?

交换机根据连接的设备类型,判断各个接口收到的数据帧是否打标,来配置交换机接口的类型。

  • 如果交换机接口收到无标记帧,由交换机根据这个接口所在 VLAN 为数据帧打上 VLAN 标签;同时接口发送数据帧时,也不携带 VLAN 标签。应该把这类接口配置为Access(接入)接口,Access接口连接的链路称为Access链路。
  • 如果交换机接口收到多个 VLAN 的流量,也就是收到了标记帧;同时为了让对端设备能够区分不同 VLAN 的流量,通过接口发出的流量会打上 VLAN 标签。应该把这类接口配置为Trunk(干道)接口,相应的链路称为Trunk链路。

跨交换机发送数据

主机 A 以主机 F 的 MAC 地址作为目的 MAC 地址封装了一个数据帧,从网卡发送出去。

交换机 A 在Access接口收到数据帧。查询 MAC 地址表,发现数据帧的目的地址是与交换机 B 相连的Trunk接口。于是交换机给数据帧打上Access接口的 PVID 配置,即给数据帧打上 VLAN 10 的标签,并从Trunk接口转发给交换机 B。

交换机 B 在trunk接口收到数据帧。查看 MAC 地址表,发现是 VLAN 10 的数据帧,目的地址设备是连接在 VLAN 10 的一个Access接口上。于是去掉数据帧的 VLAN 标签,并从这个Access接口转发给主机 F。

模拟实验

Access 接口和 Trunk 接口的配置

实验拓扑图

实验要求

  • 将 SW1(即交换机 1)和 SW2(即交换机 2)相连的接口配置为Trunk接口,允许传输 VLAN 5 的数据;
  • 将 PC(即主机)与 SW 相连接口配置为Access接口,接口的 PVID 配置为 VLAN 5。

实验步骤

SW 1 上的配置如下:

检查 SW1 的接口配置,使用命令display vlan查看接口 VLAN 情况。

Hybrid 接口的配置

三种接口类型特点:

  • Access接口:这种接口只能属于一个 VLAN,只能接收和发送一个 VLAN 的数据。通常用于连接终端设备,比如主机或服务器等。
  • Trunk接口:这种接口能够接收和发送多个 VLAN 的数据,通常用于连接交换机。
  • Hybrid接口:这种接口能够接收和发送多个 VLAN 的数据,可用于交换机的链路,也可用于终端设备。与Trunk接口的区别是,发送数据时Trunk接口只会摘掉 PVID 标签,而Hybrid接口能够不携带 VLAN 标签发送多个 VLAN 数据。

实验拓扑图

实验要求

新建 3 个 VLAN,PC1 属于 VLAN 2,PC2 属于 VLAN 3,Server 1(即服务器 1)属于 VLAN 10;

通过Hybrid接口实现 VLAN 2 和 VLAN 3 不能互通,但 VLAN 2 和 VLAN 3 都能与 VLAN 10 进行通信。

实验步骤

SW1 的E0/0/2接口,只允许通过 VLAN 2,PC1 又需要访问 VLAN 10,但是无法识别 VLAN 标签信息,因此配置Hybrid的 PVID 为 VLAN 2,同时放通 VLAN 2 和 VLAN 10。E0/0/3接口配置同理。E0/0/1接口需要放通 VLAN 2、VLAN 3 和 VLAN 10 的流量,对端交换机又需要识别 VLAN 标签,因此以带 VLAN 标签的形式放通 VLAN 2、VLAN 3 和 VLAN 10 的流量。SW1 上的配置如下:

SW2 的E0/0/1接口配置和 SW1 的 E0/0/1 接口同理。SW2 的E0/0/10接口,只允许通过 VLAN 10,Server1 又需要放通 VLAN 2 和 VLAN 3 的流量,因此配置Hybrid的 PVID 为 VLAN 10,同时放通 VLAN 2、VLAN 3 和 VLAN 10。SW2 上的配置如下:

检查 VLAN 10 信息,分别在 SW1 和 SW2 上使用命令display vlan 10查看配置是否正确。

结尾

Access接口接收数据帧处理过程:

Access接口发送数据帧处理过程:

Trunk接口接收数据帧处理过程:

Trunk接口发送数据帧处理过程:

交换机基础

常见网络设备

中继器

中继器(repeater)是一种信号增强设备,运行在 OSI 参考模型的第一层。它的功能仅仅是将信号重新输出,延迟网络的传输距离,不进行其它的数据控制,也无法识别数据链路层的 MAC 地址和网络层的 IP 地址。

网桥

网桥(bridge)是具有两个端口的二层网络设备,可隔离冲突域。作用相当于 OSI 模型中的数据链路层,能够根据 MAC 地址进行数据转发。只能连接同构网络(同一网段),不能连接异构网络(不同网段)。

集线器

集线器(hub)是工作在物理层、具有信号放大功能、以它为中心的网络设备。即一个多端口的中继器,以集线器为中心,连接多个节点。广播方式发送数据,也就是说,当它要发送数据时,会发送到与集线器相连的所有节点。

由于集线器没有控制功能,因此所有终端共享带宽,同一时刻只能一个终端发送数据,多个终端同时发送数据就会产生冲突。这时,集线器、连接线缆以及连接在集线器上的终端设备构成了一个冲突域。于是采用 CSMA/CD 方式决定终端能否发送数据。

交换机

集线器是从接收方收到的数据,会转发给所有非发送方端口,也就是简单的通过复制电气信号来实现发送。

但是交换机是通过学习连接的每个终端的 MAC 地址,将数据发送给对应的目的终端上,避免将数据发送到无关端口,提供网络利用率。这里说的交换机都是二层交换机。

如果是没有学习到的 MAC 地址,或者想跟网段内所有终端进行通信,交换机会使用广播方式,将数据帧进行泛洪。

对比集线器,交换机有哪些优点

优点 说明
隔离冲突域 冲突域只在交换机端口和主机之间
全双工通信 冲突域没有其它主机,主机能够同时发送和接收数据帧
丢弃错误帧 交换机能够检测数据帧是否有错误,并丢弃错误的数据帧
独享带宽 发送和接收分别是不同的交换机端口,每个端口独享带宽

交换机如何转发数据帧

交换机收到数据帧后,会有三种处理方法:直通转发、碎片隔离和存储转发。

直通转发

直通转发是交换机只读取数据帧的前 14 个字节就进行转发。由于读取的数据量固定,发送方和接收方的速度需要一致,导致无法桥接不同速率的以太网。另外,只读取前 14 个字节,会跳过了 FCS 域,因此无法检测并丢弃 CRC 校验错误的数据帧。

碎片隔离

碎片隔离是读取数据帧的前 64 个字节就进行转发,可以防止转发小于 64 字节的残帧。但是如果出现 CRC 错误,还是会转发数据帧。也无法桥接不同速率的以太网。

存储转发

存在转发会读取数据帧全部内容再进行转发。这样就可以识别残帧和 CRC 校验错误帧,并将它们丢弃。交换机还能对数据帧进行缓存,因此可以桥接不同速率的以太网。

转发方式 读取字节数 通信时延 丢弃错误数据帧
直通转发 14字节 最短
碎片隔离 64字节 只丢弃残帧
存储转发 全部 最长 全部

交换机功能

MAC 地址数

MAC 地址数是指一台交换机最大可以学习到的 MAC 地址表数量。

生成树功能

为了避免二层环路,我们使用生成树协议(STP),让交换机知道对方的存在,具体做法是在交换机之间交换 BPDU 数据帧。

链路聚合

链路聚合是将交换机的多条线路汇聚成一条逻辑线路在网络中使用。有多个称呼:端口聚合、链路捆绑、绑定等。

如果不使用链路聚合功能,直接将交换机的多个物理端口连接起来,可能会导致网络环路。如果使用生成树协议,又会避开某些链路,导致只有一条物理链路可用。如果使用链路聚合,把几条物理链路聚合成一条逻辑链路,即使某一条物理链路断开,由于逻辑线路还有其它物理链路在维持,因此通信也不会中断,到达线路冗余的效果。

VLAN

将广播域分割成一个个逻辑网段的功能叫做 VLAN。

端口镜像

将某个端口接收和发送的数据帧复制到镜像端口的功能叫做端口镜像,被复制的源端口叫做监控端口。

为了分析网络故障或检测网络中的流量,交换机会将收到的数据帧复制一份并转发到网络分析设备或流量监控设备中。

QoS 优先级队列

QoS 是Quality of Service的缩写,也叫做服务质量。当数据通过网络设备时,根据通信种类控制通信优先级和带宽的功能。通常是将声音、视频等数据定义为高优先级,高优先数据优先处理,保障这类数据的稳定和低延迟。

除了交换机在二层进行的 QoS 控制外,还有路由器和三层交换机的三层(IP)的 QoS 控制,以及 TCP 进行的四层的 QoS 控制。

IEEE 802.1p 标准完成了对二层的 QoS 优先级控制的标准化工作。通过 3bit 长度的优先级控制信息,定义了从 0 到 7 的 8 个优先级,即 CoS 值(服务等级值),交换机会优先转发值大的数据帧。

MAC 地址过滤

为了网络安全,只让指定的设备接入网络。二层交换机提供了以数据帧的头部信息进行过滤的功能。具体过程是,先设置一个过滤条件,比如目的 MAC 地址、源 MAC 地址等,满足条件的数据帧通过,阻断不满足条件的数据帧。

考虑到伪造 MAC 地址的情况发生,还可以跟 802.1X 一起使用。三层交换机或路由器可以根据 IP 头部信息完成 IP 通信过滤的功能。

基于端口的认证

在交换机中,只有通过认证的客户端才能使用有线端口。这个功能由 IEEE 802.1X 完成标准化,对接入 LAN 的客户端进行认证的机制。

当 PC 连接交换机时,认证过程启动。根据发送方的 MAC 地址信息进行客户端识别,通过用户名、口令或证书等认证信息进行用户认证。对于没有认证的客户端发来的数据帧,交换机只接收包含认证信息的数据帧,其余的全部丢弃。对于认证失败的客户端发来的数据帧,交换机就直接丢弃不会进行转发。

要使用基于端口的认证功能,客户端的电脑和交换机都要支持 802.1X 认证功能,缺一不可。

802.1X 认证中使用 PPP 的扩展协议 EAP,通过 EAPOL 协议封装 EAP 认证消息,然后在 LAN 中进行传输。认证结束之前,客户端电脑只能进行 EAPOL 通信。

网络管理

远程管理、监控和配置网络设备可以使用 SNMP 协议。SNMP 协议可以对整个网络结构内的交换机和其它网络设备进行集中统一的管理。

被 SNMP 管理的网络设备叫做Agent,管理网络的设备叫做Manager

交换机的架构

交换机的基本架构是由 RJ-45 接口、PHY 、MAC 等模块的 NIC 和管理由 NIC 收发帧缓存、转发表的软件组成,通过查看转发表信息,在 NIC 之间进行数据帧交互。

交换机分类

根据功能分类

交换机按照功能可以分为二层交换机和三层交换机。

  • 二层交换机:没有 IP 路由功能、仅处理数据链路层的交换机叫做二层交换机。
  • 三层交换机:带有 IP 路由功能的交换机叫做三层交换机,但它是二者的有机结合,并不是简单地把路由器设备的硬件及软件叠加在局域网交换机上。三层交换机工作在网络层,可以处理数据包。

根据外形分类

根据外形,交换机可分为桌面式交换机、箱式交换机和机框式交换机。

桌面式交换机

桌面式交换机是指放在桌面上使用的交换机。它体积不大,只能连接几台网络设备,通常用于家庭网络中,主要有 3 端口、5 端口、8 端口和 16 端口的产品。

桌面式交换机通常不安装风扇,采用无风扇设计,运行噪声小。

箱式交换机

箱式交换机通常高度是 1U 或 2U,可以安装在 19 英寸的机柜内。通常采用金属外壳、内置电源,并配置冷却风扇。下行有 24 千兆网口或 48 千兆网口,上行有 2 万兆光口或 4 万兆光口的配置较多。下行使用 RJ-45 的网线接口,上行使用 SFP+ 槽进行连接。

主要作为企业中作为接入交换机使用,支持电源冗余。

框式交换机

框式交换机是指在机框内组合多个接口模块的交换机。可以根据需要选择端口数量和不同类型的接口模块,扩展性好,端口数量多。

在机框中可以添加电源、风扇等组成部分,再插入管理模块和接口模块。接口模块和管理模块叫做线卡。机框上总线的主板叫做背板,可以插入线卡。

根据用途分类

根据交换机在网络中的位置和用途,可分为三类:核心交换机(核心层)、汇聚交换机(汇聚层)和接入交换机(接入层)。

核心交换机不是某种网络交换机,它是指位于网络主干或物理核心的数据交换机,因此,它必须是一个大容量的交换机,以作为通往广域网(WAN)或互联网的网关,总之,它为网络提供了最终的聚合点,并允许各种聚合模块协同工作。

汇聚交换机位于汇聚层,向上连接核心交换机,向下连接到接入交换机,也称为分布交换机,作为核心层交换机和接入层交换机之间的桥梁。

此外,汇聚交换机确保数据包在企业网络中的子网和 VLAN 之间正确路由。

接入交换机一般位于接入层,用于将大多数设备连接到网络,因此通常具有高密度端口,它是最常用的千兆以太网交换机,直接与公共互联网通信,主要用于办公室、小型服务器机房和媒体制作中心,托管和非托管交换机都可以部署为接入层交换机。

交换机可以共存于同一网络中,并相互协调以实现不受限制的网络速度,每个层交换机执行自己的职责。

名称 说明
核心交换机 当网络分布在多个大楼时,各个大楼的汇聚交换机通过大楼间的核心交换机完成高速交换,通常使用三层交换机
汇聚交换机 汇聚交换机将接入交换机按照楼层为单位进行集中,使用三层交换机完成 VLAN 间的交换,有时会省去这一层,仅使用核心层和接入层搭建网络
接入交换机 直接连接用户的电脑、话机等终端的交换机,通常部署在各个楼层中

交换机端口类型

千兆以太网端口

大部分交换机都配置了 RJ-45 的千兆以太网接口,连接千兆接口要使用增强型 5 类双绞线。通过自适应功能,还可以连接百兆接口。

光纤端口

箱式交换机会配置光纤端口,主要是用于连接上行链路。为了连接万兆以太网的上行链路,通常会搭载 SFP+ 接口。

框式交换机中,一般会配置多个千兆以太网 SFP 或万兆以太网 SFP+ 接口的接口卡。

PoE 端口

接入交换机还会配 PoE 端口。PoE 端口使用网线连接 IP 电话或无线 AP,并通过网线对设备进行供电。

为了让 IP 电话或无线 AP 无需外接电源也能接入网络,通过一根网线给设备供电的技术就是 PoE 技术。

上行链路端口

接入交换机和汇聚交换机要集中下行连接的所有设备流量,并将流量传输到上行的网关或核心交换机中,向网关、核心交换机传输流量的端口叫做上行链路端口,反向就叫做下行链路端口。在箱式交换机中一般会配置 2~4 个万兆上行链路端口。

下行链路端口

通常下行链路是 RJ-45 的接口,也有使用光纤接口的。一台交换机或一块板块,能提供 24 或 48 个接口。

交换机的处理能力的指标

交换机的处理能力也叫做背板容量或交换机容量。容量单位是bit/s(比特每秒),值越大,说明交换机在单位时间内传输的数据越多。

当交换机的所有端口的总带宽小于交换机的容量时,交换结构为非阻塞,即带宽充裕,没有等待处理的情况。反之,当所有端口总带宽超过交换机的容量时,叫做交换机结构过载。

交换机是千兆端口时,处理能力达到端口数 × 2 × 1Gbit/s的数值,就是非阻塞。其中×2表示上行和下行都是1Gbit/s的全双工通信。假如交换机有 24 个端口,背板容量到达24 × 2 × 1G = 48Gbit/s,就是非阻塞。

OSPF详解

RIP 缺陷

提及 OSFP 时,还得先从 RIP 说起。

RIP 以跳数来计算到达目的网络的最优路径,在实际应用时并不合适,以网络带宽和链路时延来衡量网络质量会更合理。

RIP 支持的最大跳数是 16,无法用于搭建大规模的网络。

RIP 的收敛速度慢,RIP 会对不可达路由的信息更新进行抑制,原路由失效,新路由要等到抑制时间结束后,才能更新。

RIP 的更新周期长,一个路由器突然离线,其它路由器要很长时间才能发现。

RIP 使用广播发送全部路由信息,网络规模越大,路由信息占用的网络资源也越大。

因此,RIP 路由并不适合大规模的网络,而 OSPF 协议解决了这些问题,得到了广泛的使用。

OSPF 原理

OSPF,是Open Shortest Path First的缩写,译为开放最短路径优先。OSPF 是基于链路状态(Link State)的自治系统内部路由协议,用来替代 RIP 协议,通用的是 OSPFv2。

与距离矢量协议不同,链路状态协议使用最短路径优先算法(Shortest Path First,SPF)计算和选择路由。这类路由协议关系网络链路或接口的状态,比如 up、down、IP 地址、掩码、带宽、利用率和时延等。每台路由器将已知的链路状态向其它路由器通告,让网络上每台路由器对网络结构有相同的了解。然后,路由器以此为依据,使用 SPF 算法计算和选择路由。

OSPF 协议使用组播发送协议包,节约资源,又减少对其它网络设备的干扰。

OSPF 将协议包封装在 IP 包中,协议号 89。由于 IP 协议是无连接的,OSPF 定义了一些机制保证协议包安全可靠的传输。

总之,OSPF 协议比 RIP 有更大的扩展、快速收敛和安全可靠等特性,采用路由增量更新保证路由同步,减少对网络资源的浪费。

OSPF 协议简介

OSPF 协议有四个主要过程:

  • 寻找邻居
    OSPF 协议启动后,先寻找网络中的邻居,也就是通过Hello报文确认可以双向通信。
  • 建立邻接关系
    一部分路由器形成邻居关系后,就开始进行建立邻接关系。建立了邻居关系的路由器才能互相传递链路状态信息。
  • 链路状态信息同步
    建立邻接关系的 OSPF 路由器在网络中交互 LSA(链路状态通告),最后形成包含网络完整链路状态信息的 LSDB(链路状态数据库)。
  • 计算路由
    LSDB 同步完成后,OSPF 区域内的每个路由器对网络结构有相同的认识,邻居路由器之间形成完全的邻接关系。然后,每台路由器根据 LSDB 的信息使用 SPF(最短路径优先)算法独立计算出路由。

OSPF 协议过程

将 OSPF 的四个过程展开来讲,就是一个个邻居状态的切换,不同的邻居状态有不同的行为。

Down(失效)

OSPF 邻居的初始状态,表示接口没有收到邻居发来的Hello报文。

Init(初始)

收到邻居发送的Hello报文,但是报文内没有自己的Router-ID,邻居状态就是Init。这个状态表示,直连链路上有一个 OSPF 路由器,但是还未确认双向通信。接下来,路由器会把对方的Router-ID添加到发送的Hello报文中。

Attempt(尝试)

只在 NBMA 网络中出现。当路由器的 NBMA 接口启动后,邻居状态从Down切换到Attempt。这种状态下,路由器周期性的向邻居发送Hello报文,但是未收到邻居的有效Hello报文。当路由器收到邻居发送的没有自己Router-IDHello报文后,就将邻居状态切换到Init

2-Way(双向通信)

路由器收到邻居的Hello报文,报文里有自己的Router-ID时,状态切换成2-Way,表示两个路由器形成了可以双向通信的邻居关系。

选举 DR 和 BDR

如果路由器是在一个 MA 网络,邻居状态在2-Way后,会进行 DR 和 BDR 选举。

ExSart(交换开始)

接来下,路由器会进入ExStart状态,发送空的 DD 报文,用于协商Master/SlaveRouter-ID最大的路由器成为Master路由器,DD 报文的序列号由Master路由器决定。协商Master/Slave的报文是空的、不携带 LSA 头部的 DD 报文,这时报文的 I 位被设置成 1。

Exchange(交换)

接下来,路由器进入Exchange状态,向邻居发送描述自己 LSDB 的 DD 报文,DD 报文中包含 LSA 头部。DD 报文逐个发送,每个报文中都有 DD 序列号,DD 序列号由Master路由器决定,序列号在 DD 报文的交互过程中递增,确保交互过程的有序性和可靠性。

Loading(加载)

接下来,路由器进入Loading状态,路由器向邻居发送 LSR 请求 LSA 的完整信息。邻居使用 LSU 进行回应,LSU 报文里有 LSA 的完整信息。在收到 LSU 报文后,路由器需要发送 LSAck 对 LSA 进行确认。

Full(完整)

当接口上需要请求的 LSA 列表为空时,表示路由器已经完成了和邻居的 LSDB 同步,没有再需要请求的 LSA 了,这时邻居的状态就是Full

路由计算
接下来,路由器开始计算路由。先评估一台路由器到另一台路由器需要的度量值。OSPF 协议是根据路由器的每一个接口的度量值决定最短路径的。一条路由的开销是指到达目的网络的路径上所有路由器接口的度量值总和。

度量值和接口带宽有关,路由器的接口度量值是根据公式 100/带宽(Mbps)计算出来的,它作为评估路由器之间网络资源的参考值。另外也可以通过命令手工指定路由器的度量值。

MA 和 NBMA 网络

MA 网络,即多路访问网络,是在同一个共享介质中连接多个设备的网络。网络中的任意两台设备都能直接进行二层通信。MA 网络有两种,一种是 BMA 网络,即广播型多路访问网络,比如以太网,典型场景就是一台以太网交换机连接着多台路由器,如果有一个广播数据发出来,整个网络中的路由器都能收到。另一种是 NBMA 网络,即非广播型多路访问网络,NBMA 类型的网络已经看不到了,它允许多台路由器接入,但是没有广播能力,无法使用组播或广播,只能配置成单播发送 OSPF 报文。帧中继、X.25 都是这类网络。

DR 和 BDR

在 MA 网络中,n台路由器都两两建立邻接关系,那么就有n(n-1)/2个邻接关系,会消耗大量的路由器资源,增加网络中 LSA 的泛洪数量。为了优化邻接关系数量,减少不必要的协议流量,OSPF 会在每一个 MA 网络中选举一个 DR(指定路由器)和一个 BDR(备用指定路由器)。

既不是 DR 也不是 BDR 的路由器叫做 DROther,MA网络中所有 DROther 只和 DR 及 BDR 建立 OSPF 邻接关系,BDR 也和 DR 建立邻接关系,DROther 之间只停留在2-Way状态。这样,就有2(n-2)+1个邻接关系,数量得到优化。

DR 在 LSDB 同步方面有关键性的作用,会侦听网络中的拓扑变化信息,并将变更信息通知给其它路由器。DR 会生成一种 Type-2 LSA,这个 LSA 包含个 MA 网络中所有 OSPF 路由器的Router-ID,也包括 DR 自己的。BDR 会监控 DR 状态,当 DR 发生故障时就接替它的工作。

DR、BDR 的选举通过Hello报文实现,发生在2-Way状态之后。Hello报文有路由器接口的 DR 优先级,取值范围是0~255,默认值为 1,DR 优先级为 0 的接口没有 DR 和 BDR 的选举资格。

当接口激活 OSPF 后,它会查看网络中是否存在 DR,如果有就使用已经存在的 DR,也就是 DR 不可抢占,否则选择最高优先级的路由器成为 DR,当优先级相等时,选择Router-ID最大的路由器成为 DR。之后还会进行 BDR 的选举,选举过程与 DR 类似。

需要注意的是,DR 和 BDR 是一个接口级别的概念。某台路由器是 DR,这种说法不准确,严谨的说法是:某台路由器的某个接口在这个 MA 网络中是 DR。

在一个 MA 网络中,DR 要确保接入到网络中的所有路由器有相同的 LSDB,也就是确保 LSDB 同步。DR 使用组播地址224.0.0.5向网络中发送 LSU 报文,所有 OSPF 路由器都会侦听这个组播地址,并与 DR 同步 LSDB。而 DROther 感知到拓扑变化时,向224.0.0.6发送 LSU 报文通告这个变化,DR 和 BDR 会侦听这个组播地址。

度量值

每种路由协议对度量值的定义是不同的,OSPF 使用Cost(开销)作为路由度量值,Cost值越小,则路径(路由)越优。每一个激活 OSPF 的接口都有一个接口的Cost值,值等于 100/接口带宽 Mbit/s,计算结果取整数部分,当结果小于 1 时,值取 1。这个值也可以人为修改,修改值会直接影响Cost值的计算,从而影响网络中 OSPF 路由的选择。

同步 OSPF 区域内每台路由器的 LSDB,路由器通过交互 LSA 实现 LSDB 的同步。LSA 不但携带了网络连接状况信息,而且携带各接口的Cost信息。

由于一条 LSA 是对一台路由器或一个网段拓扑结构的描述,整个 LSDB 就形成了对整个网络的拓扑结构的描述。所有路由器得到一张完全相同的图。

使用 SPF(最短路径优先算法)计算出路由。OSPF 路由器用 SPF 算法以自己为根节点,计算出一棵最短路径树。这棵树上,由根到各个节点的累计开销最小,也就是从根到各个节点的路径都是最优的,这样就获得了由根去往各个节点的路由。计算完成后,路由器将路由加入到 OSPF 路由表。当 SPF 算法发现有两条到达目的网络的路由的Cost值相同,会将这两条路由都加入到 OSPF 路由表中,形成等价路由。

Router-ID

Router-ID用于标识 OSPF 路由器,是一个 32 位的数值,跟 IPv4 地址格式一样。连续的 OSPF 路由器组成的网络叫做 OSPF 域,域内Router-ID必须唯一,也就是在同一个域内不允许出现两台相同Router-ID的路由器。Router-ID可以手动设置,也可以自动生成,常见的做法是把设备的Router-ID指定为设备的Loopback接口的 IP 地址。

Loopback 接口

Loopback接口也就是本地回环接口,是一种软件的、逻辑的接口,不只网络设备支持Loopback接口,Windows 主机或 Linux 主机也支持。根据业务需求,在网络设备上创建Loopback接口,并配置 IP 地址。Loopback接口非常稳定,除非手动进行关闭或删除,否则是永远不会失效的。正因如此,Loopback接口常用于设备网管、网络测试、网络协议应用等。

1
2
3
4
5
6
7
# 为设备创建一个 Loopback 接口,并配置接口的 IP 地址
[router]interface loopback 0
[router-loopback0]ip address 1.1.1.1 32
[router-loopback0]quit

# 创建一个 OSPF 进程,并配置设备的 Router-ID 为 1.1.1.1
[router]ospf 1 router-id 1.1.1.1

报文类型及格式

OSPF 协议的报文直接使用 IP 封装,在 IP 报文头部对应的协议号是 89。通常 OSPF 的协议报文使用组播地址作为目的 IP 地址,有两个组播 IP 地址是 OSPF 专用。

  • 224.0.0.5:这个组播 IP 地址是指所有的 OSPF 路由器。
  • 224.0.0.6:这个组播 IP 地址是指所有的 OSPF DR 路由器。

OSPF 一共定义了五种报文,各有各的用途。

类型 报文名称 报文说明
1 Hello 用于发现直连链路上的 OSPF 邻居,以及维护 OSPF 邻居关系
2 DD
Database Description
数据库描述
用于描述LSDB,报文中携带的是 LSA 的头部数据,不是完整的 LSA 内容
3 LSR
Link State Request
链路状态请求
向 OSPF 邻居请求 LSA
4 LSU
Link State Update
链路状态更新
用于发送 LSA,报文中携带的是完整的 LSA 数据
5 LSAck
Link State Acknowledgment
链路状态确认
设备收到 LSU 后,LSAck对接收的 LSA 进行确认

路由器的接口一旦激活 OSPF,就会开始发送Hello报文。Hello报文的一个重要功能就是发现直连链路上的 OSPF 邻居。发现邻居后,就开始邻接关系的建立。这个过程中,DD 报文用于发送 LSA 的头部摘要。通过 DD 报文的交互,路由器知道了对方所有的 LSA,而 LSR 向对方请求完整的 LSA。LSU 对 LSR 进行回应,或者主动更新 LSA,LSU 包含完整的 LSA 数据。LSAck 保证 OSPF 更新机制的可靠性。此外,Hello报文负责 OSPF 邻居关系的维护,两台直连路由器形成邻接关系后,双方仍然周期性的发送Hello报文,告知对方自己是在线状态。

所有 OSPF 报文是相同的头部,这个头部的长度是 24 字节。

  • 版本:OSPFv2 的值为 2。
  • 类型:表示 OSPF 报文的类型。值与报文类型对应关系是:1–Hello;2–DD;3–LSR;4–LSU;5–LSAck
  • 报文长度:整个 OSPF 报文的长度,单位是字节。
  • 路由器 ID:路由器的 OSPF Router-ID
  • 区域 ID:表示所属的区域 ID,是一个 4 字节的数值。
  • 校验和:用来校验报文有效性。
  • 认证类型:表示报文使用的认证类型。
  • 认证数据:用于报文认证的内容。

Hello 报文

Hello报文用于发现直连链路上的邻居,以及维护邻居关系。Hello报文携带邻居关系建立的各项参数,建立邻居关系的过程中,会检查这些参数,只有参数匹配,才能正确建立邻居关系。

  • 网络掩码:这个字段表示接口的网络掩码。如果两台路由器是通过以太网接口连接,那么直连的两个接口必须配置相同的网络掩码。如果收到的Hello报文中“网络掩码”字段与自己接口的不同,就忽略这个Hello报文,不会建立邻居关系。
  • Hello间隔:接口周期性发送Hello报文的时间间隔,单位是秒。两台路由器要建立邻居关系,需要接口的Hello间隔相同,否则邻居关系无法建立。默认情况下,OSPF 路由器在 P2P 或 Broadcast 类型的接口上,Hello间隔是 10 秒,在 NBMA 及 P2MP 类型的接口上,Hello间隔是 30 秒。
  • 选项:这个字段一共 8 比特,每个比特位都表示路由器的某个特性。路由器通过设置相应的「选项」比特位来通告自己支持某种特性或拥有某种能力。
  • 路由器优先级:路由器优先级,也叫做 DR 优先级,用于 DR 和 BDR 的选举。默认情况下,OSPF 接口的 DR 优先级是 1,这个值也可以通过命令进行修改。
  • 路由器失效时间:路由器等待对方发送Hello报文的时间,超过这个时间就认为是路由器已离线。路由器建立邻居关系,也需要双方接口的路由器失效时间相同。默认情况下,路由器失效时间是Hello间隔的 4 倍。
  • 指定路由器:网络中 DR 的接口 IP 地址。如果值为0.0.0.0,表示没有 DR`,或 DR 还未选举出来。
  • 备份指定路由器:网络中 BDR 的接口 IP 地址。如果值为0.0.0.0,表示没有 BDR,或 BDR 还未选举出来。
  • 邻居:表示邻居的Router-ID,是在直连链路上发现的有效邻居,如果发现多个邻居,就包含多个邻居字段。

DD 报文

DD 报文用于描述 LSDB,这个报文携带的是 LSDB 中 LSA 的头部数据,并非完整的 LSA 内容。互为邻居的路由器使用空的 DD 报文来协商主/从(Master/Slave),空的 DD 报文不包含任何 LSA 头部信息。Router-ID更大的路由器成为Master路由器。

Master/Slave确定后,双方开始使用 DD 报文描述各自的 LSDB,这时的 DD 报文包含 LSDB 里的 LSA 头部信息。路由器可以使用多个 DD 报文来描述 LSDB,为了确保 DD 报文传输的有序和可靠,Master路由器使用“DD Sequence Number(DD 序列号)”字段主导整个 LSDB 交互过程。比如:Master路由器发送一个 DD 序列号是 100 的 DD 报文给Slave路由器,Slave收到这个报文后,才发送自己的 DD 报文,而 DD 序列号也使用 100。Master路由器发送下一个 DD 报文(DD 序列号是 101),Slave路由器才会发送 DD 报文。这个过程一直持续,直到 LSDB 同步完成。

  • 接口最大传输单元(Interface Maximum Transmission Unit):接口的 MTU。默认情况下,接口发送的 DD 报文中,无论接口实际的 MTU 值是多少,值都为 0。
  • 选项:路由器支持的 OSPF 可选项。
  • I 位(Initial Bit):初始化位,协商Master/Slave路由器时,值为 1,Master/Slave选举完成后,值为 0。
  • M 位(More Bit):如果值为 1,表示后续还有 DD 报文;如果值为 0,表示这是最后一个 DD 报文。
  • MS 位(Master Bit):Master路由器发送的 DD 报文中,值为 1,Slave路由器则值为 0。
  • DD 序列号:DD 报文的序列号,在 DD 报 文交互过程中,逐次加 1,确保传输的有序和可靠。DD 序列号必须由Master路由器决定,而Slave路由器只能使用Master路由器发送的 DD 序列号来发送自己的 DD 报文。
  • LSA 头部:当路由器使用 DD 报文描述自己的 LSDB 时,LSA 头部信息就在这里。一个 DD 报文可能包含一条或多条 LSA 头部信息。

LSR 报文

在与 OSPF 邻居交换 DD 报文后,路由器就知道了邻居的 LSDB 摘要,向邻居发送 LSR 报文请求所需 LSA 的完整数据。LSR 报文的链路状态类型(Link-State Type)、链路状态 ID(Link-State ID)、通告路由器(Advertising Router)三个字段表示路由器请求的 LSA。如果请求多个 LSA,那么 LSR 可能包含多个三元组。

  • 链路状态类型:表示 LSA 类型。OSPF 有多种 LSA 类型,每种 LSA 描述 OSPF 网络的某个部分,使用不同的类型编号。常见的 LSA 类型值和 LSA 名称是:1–Router LSA,2–Network LSA,3–Network Summary LSA,4–ASBR Summary LSA,5–AS External LSA
  • 链路状态标识:LSA 的标识。不同的 LSA 类型,字段的定义不同。
  • 通告路由器:生成这条 LSA 的路由器的Router-ID

LSU 报文

路由器收到邻居发送的 LSR 后,会使用 LSU 报文进行回应,在 LSU 报文中包含请求 LSA 的完整信息,一个 LSU 报文可以包含多个 LSA。另外,当路由器感知到网络发生变化时,也会触发 LSU 报文的泛洪,及时把网络变化通告给其它路由器。在 BMA 网络中,非 DR、BDR 路由器向组播地址224.0.0.6发送 LSU 报文,而 DR 和 BDR 会侦听这个组播地址,DR 在接收 LSU 报文后向224.0.0.5发送 LSU 报文,从而将更新信息泛洪到整个 OSPF 区域,所有的 OSPF 路由器都会侦听224.0.0.5这个组播地址。

LSAck 报文

当一台路由器收到邻居发送的 LSU 报文时,为了确认 LSA 已经送达,需要对报文中的 LSA 进行确认,就是回复一个 LSAck 报文。LSAck 报文包含路由器确认的 LSA 头部信息。

OSPF 三张表

OSPF 使用三种表格确保能正常运行。

邻居表(Peer Table)

在 OSPF 交互 LSA 之前,两台直连路由器需要建立 OSPF 邻居关系。当一个接口激活 OSPF 后,就会周期性的发送 OSPF Hello报文,同时侦听Hello报文从而发现直连链路上的邻居。在接口上发现邻居后,邻居的信息会写入路由器的 OSPF 邻居表,随后一个邻接关系的建立过程也开始了。

OSPF 路由器在网络中泛洪的链路状态信息,叫做 LSA(Link-State Advertisement,链路状态通告)。路由器搜集 LSA 并添加到自己的 LSDB 中,路由器通过 LSDB 获取网络的完整信息。OSPF 定义了多种类型的 LSA ,这些 LSA 各有用途,最终目的是让路由器知道网络的拓扑结构以及网段信息,并计算出最短路径树,从而发现到达全网各个网段的路由。

OSPF 路由表(Routing Table)

OSPF 根据 LSDB 中的数据,运行 SPF 算法,得到一棵以自己为根、无环的最短路径树,基于这棵树,OSPF 能够发现到达网络中各个网段的最佳路径,从而得到路由信息,并添加到 OSPF 路由表中。当然,这些 OSPF 路由表中的路由最终是否被添加到全局路由器,还需要经过比较路由优先级等过程。

邻接关系

OSPF 有两个概念:邻居关系和邻接关系。假如两台路由器通过网线直连,在双方互联的接口上激活 OSPF,路由器接口开始发送和侦听Hello报文,通过Hello报文发现彼此,并确认双向通信后,就形成了邻居关系。

之后,两台路由器会开始交互空的 DD 报文协商Master/Slave,再交互包含 LSA 头部信息的 DD 报文,以便同步自己的 LSDB,然后通过 LSR 和 LSU 报文交互双方的 LSA。当两者的 LSDB 同步完成后,两台路由器形成对网络拓扑的一致认知,并开始独立计算路由。这时,两台路由器形成了邻接关系。

网络类型

OSPF 的许多功能或特性都是基于接口实现的,当一个接口激活 OSPF 后,这个接口会维护很多 OSPF 变量,比如:接入的区域 ID 、接口 Cost 值、DR 优先级、邻居列表、认证类型等,其中接口的网络类型( Network-Type )是非常重要的一个变量。OSPF 接口的网络类型跟接口的数据链路层封装有关,在不同网络类型的接口上,OSPF 的操作有所不同。

1、点对点类型( Point-to-Point ,P2P )

P2P 网络是在一条链路上只能连接两台路由器的环境。典型的例子就是 PPP 链路,当两台路由器通过 PPP 链路直连时,接口的封装协议就是 PPP ,接口激活 OSPF 后,网络类型就是 P2P 。OSPF 在 P2P 网络类型中,接口以组播方式发送协议报文,组播地址是 224.0.0.5 ,报文类型包括 Hello 报文、DD 报文、LSR 报文、LSU 报文和 LSAck 报文。默认情况下,P2P 类型接口的 Hello 报文发送间隔是 10 秒。P2P 类型的网络中,不会选举 DR 和 BDR 。

2、广播型多路访问类型(Broadcast Multi-Access ,BMA)

BMA 网络中可以多台路由器接入,任意两台路由器之间都能进行二层通信,一台路由器发送出去的广播数据,其它所有路由器都能收到,是一个支持广播的网络环境。以太网就是典型的 BMA 网络。当多台路由器接入到 BMA 网络时,比如多台路由器连接在同一台二层交换机上,这些路由器的接口激活 OSPF 就会开始发送组播的Hello报文,从而发现网络中的其它路由器。BMA 网络中,会选举 DR 和 BDR,所有非 DR、BDR 路由器仅与 DR 和 BDR 建立邻接关系。

OSPF 在 BMA 网络中,接口以组播方式发送Hello报文、LSU 报文以及 LSAck 报文,单播方式发送 DD 报文及 LSR 报文。当路由器需要向 DR 和 BDR 发送 OSPF 报文时,使用224.0.0.6这个组播地址作为目的 IP 地址;当需要向所有的 OSPF 路由器发送报文时,使用224.0.0.5。默认情况下,广播类型接口的Hello报文发送间隔是 10 秒。

3、非广播型多路访问类型(Non-Broadcast Multi-Access ,NBMA)

NBMA 网络也允许多台路由器接入,但是不具备广播能力,这时组播发送的Hello报文在 NBMA 网络中可能会有问题。为了让 OSPF 路由器之间能够顺利发现彼此,并正确建立邻接关系,还需要手动配置,比如使用单播方式发送 OSPF 报文等。帧中继、X.25 就是 NBMA 网络,不过现在已经几乎看不到这类型网络了。NBMA 网络中,也会进行 DR 和 BDR 选举。默认情况下,NBMA 类型接口的Hello报文发送间隔是 30 秒。

4、点对多点类型(Point-to-Multipoint ,P2MP)

P2MP 网络中,路由器接口的数据链路层封装不会自动设置,必须手动指定。P2MP 类似将多条 P2P 链路的一头进行捆绑的网络。在 P2MP 网络中无需选举 DR、BDR。OSPF 在 P2MP 类型的接口上以组播方式发送Hello报文,以单播方式发送其它报文。默认情况下,Hello报文发送间隔是 30 秒。

了解了这么多的网络类型,即使两个路由器的直连接口的网络类型不同,也能建立 OSPF 邻接关系,但是 OSPF 路由计算容易出现问题,因为网络类型会影响 LSA 对接口的描述,关系到路由器对网络拓扑的理解和路由计算。因此,OSPF 邻接的路由器,互联接口的网络类型必须一致。

即使在以太网中只有两台路由器,OSPF 也会选举 DR 和 BDR,实际上没必要且浪费时间,因为从逻辑上看是点对点的连接,选举 DR 和 BDR 实在是画蛇添足。因此,为了提高 OSPF 的效率,加快邻接关系的建立过程,可以把互联接口的网络类型修改为 P2P。

区域和多区域

连续的 OSPF 路由器组成的网络叫做 OSPF 域(Domain),为了保证每台路由器都能正常的计算路由,就要求域内所有的路由器同步 LSDB,才能达到对整个 OSPF 网络的一致认知。当网络规模越来越大时,每台路由器维护的 LSDB 变得臃肿,计算庞大的 LSDB 需要消耗更多的设备资源,加重设备的负担。另外,网络拓扑的变化,引起所有域内的路由器重新计算,而域内路由无法进行汇总,每台路由器需要维护的路由表也越来越大,又是一个不能忽略的资源消耗。

因此,OSPF 引入了区域(Area)的概念。在一个大规模的网络中,会把 OSPF 域分成多个区域。某些 LSA 的泛洪只在单个区域内部,同一个区域内的路由器维护一套相同的 LSDB ,对区域内的网络有一致的认知。每个区域独立进行 SPF 计算,区域内的拓扑结构对区域外是不可见的,而且区域内部的拓扑变化通知被限制在区域内,避免对区域外部造成影响。如果一台路由器的多个接口分别接入多个不同的区域,那么它会为每个区域分别维护一套 LSDB 。多区域的设计极大程度的限制了 LSA 的泛洪,有效的把拓扑变化的影响控制在区域内,另外在区域边界路由器上可以进行路由汇总,减少网络中的路由条目数量。

OSPF 的每一个区域都由一个编号,不同的编号表示不同的区域,这个区域编号也叫做区域 ID(Area-ID)。区域 ID 是一个 32 位二进制数,与 IPv4 地址的格式一样,比如Area 0.0.0.1,为了方便起见,也会用十进制数表示,Area 0.0.0.1简化成Area1Area 0.0.0.255简化成Area255Area 0.0.1.0简化成Area256

一个 OSPF 域中,允许存在多个区域,其中有一个中心区域,也就是骨干区域Area0(或Area 0.0.0.0)。OSPF 要求域内的所有非骨干区域(区域 ID 不是 0 的区域)必须与Area0相连。如果一个域内有多个区域,那么有且只有一个Area0Area0负责在区域之间发布区域间的路由。因此,所有的 ABR(Area Border Router,区域边界路由器)至少有一个接口属于Area0,所以Area0包含所有的 ABR。有点类似星型结构,骨干区域在中间,每个非骨干区域是分支。

任何一个非骨干区域都必须与Area0相连,当网络中某个区域没有与Area0相连时,这个区域的路由计算就会出问题。OSPF 的区域间路由都由Area0中转,任何两个非骨干区域之间是不能直接交互路由的。

解决方法是修改 OSPF 的网络设计,与Area0直接相连。如果不能改或改动成本大等问题,可以考虑使用 OSPF 虚链路(Virtual Link)。Virtual Link是一种逻辑的链路,不是一条真实的链路。通过搭建一条Virtual Link,可以把原来没有与骨干区域直连的区域给连接起来。

另一个可能的问题是,骨干区域不连续或被分隔开。非骨干区域交互区域路由时,容易引发路由环路。因此,OSPF 要求 ABR 只能将自己直连的区域内部路由通告给Area0,而不能将自己到达其它区域的域间路由通告给Area0。另外,ABR 可以将自己直连区域的内部路由和到达其它区域的域间路由通告给非骨干区域。这样就能规避网络规划不合理导致的路由环路。解决问题最好的办法是修改 OSPF 的规划,当然建立Virtual Link也可以临时解决这个问题。

实际部署中,Virtual Link 并不是一种常规的技术,而是一种临时方案,合理的 OSPF 网络规划依然是一个最佳的选择。

OSPF 路由器角色

在 OSPF 中,有多种路由器角色,在 OSPF 网络中都发挥着不同的作用。实际上,OSPF 不仅在路由器上使用,许多交换机、防火墙,甚至 Linux 主机都能实现。这里说的 OSPF 路由器,实际上是以路由器为代表。

内部路由器(Internal Router,IR):所有接口都在同一个 OSPF 区域内的路由器。图中 R1、R4、R5 是 IR。

区域边界路由器(Area Border Router,ABR):接入多个区域的路由器,并非所有接入多个区域的路由器都是 ABR。它至少有一个接口在Area0中,同时还有其它接口在其它区域中。ABR 负责在区域之间传递路由信息,因此必须连接到Area0,同时连接着其它区域。图中 R2、R3 是 ABR。

骨干路由器(Backbone Router,BR):至少有一个接口接入Area0的路由器,那它就是一台骨干路由器,另外 ABR 也是骨干路由器。图中 R1、R2、R3、R6 是 BR。

AS 边界路由器(AS Boundary Router,ASBR):工作在 OSPF 自治系统(Autonomous System ,AS)边界的路由器。ASBR 将 OSPF 域外的路由引入到本域,外部路由在整个 OSPF 域内传递。并不是同时运行多种路由协议的路由器就一定是 ASBR,ASBR 一定是将外部路由重分发到 OSPF,或者执行了路由重分发操作的路由器。图中 R6 是 ASBR。

  • Copyrights © 2017-2023 WSQ
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信